package com.atwisdom.star.core.bsl.cost.Impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.atwisdom.star.common.util.DateUtils;
import com.atwisdom.star.common.util.TypeUtils;
import com.atwisdom.star.common.vo.JsonResult;
import com.atwisdom.star.common.vo.ModelType;
import com.atwisdom.star.common.vo.PageInfo;
import com.atwisdom.star.common.vo.ResultInfo;
import com.atwisdom.star.core.bsl.cost.CostBslService;
import com.atwisdom.star.core.bsl.planner.Impl.ProductBomBslServiceImpl;
import com.atwisdom.star.core.bsl.planner.entity.ProductBomMaster;
import com.atwisdom.star.core.bsl.planner.entity.ProductBomSub;
import com.atwisdom.star.core.dao.pojo.modelClass.hier.HierInstance;
import com.atwisdom.star.core.dao.pojo.modelClass.staticLogic.StaticLogicInstance;
import com.atwisdom.star.core.dsl.modelClass.hier.HierInstanceDslService;
import com.atwisdom.star.core.dsl.modelClass.logic.Impl.LogicInstanceDslServiceImpl;
import com.atwisdom.star.core.dsl.modelClass.resource.Impl.ResourceInstanceDslServiceImpl;
import com.atwisdom.star.core.dsl.modelClass.staticLogic.Impl.StaticLogicInstanceDslServiceImpl;
import com.atwisdom.star.exception.pojo.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName: CostBslServiceImpl
 * @ Description:
 * @author:王红艳
 * @date:
 */
@Slf4j
@Service
public class CostBslServiceImpl implements CostBslService {
    private LogicInstanceDslServiceImpl logicInstanceDslService;
    private HierInstanceDslService hierInstanceDslService;
    private ResourceInstanceDslServiceImpl resourceInstanceDslService;
    @Autowired
    private StaticLogicInstanceDslServiceImpl staticLogicInstanceDslService;
    @Autowired
    private ProductBomBslServiceImpl productBomBslService;

    @Autowired
    public void setLogicInstanceDslService(LogicInstanceDslServiceImpl logicInstanceDslService) {
        this.logicInstanceDslService = logicInstanceDslService;
    }

    @Autowired
    public void setHierInstanceDslService(HierInstanceDslService hierInstanceDslService) {
        this.hierInstanceDslService = hierInstanceDslService;
    }

    @Autowired
    public void setResourceInstanceDslService(ResourceInstanceDslServiceImpl resourceInstanceDslService) {
        this.resourceInstanceDslService = resourceInstanceDslService;
    }
    //每天的毫秒数
    private static long day = 1000 * 60 * 60 * 24;
    //日期字符串的格式
    private static DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //会计期className
    private static String AccountPeriodClassName = "AccountPeriod";
    /**
     * 会计期新增接口
     *
     * @return 返回新增会计期的数据
     */
    public ResultInfo<?> addFiscalPeriod(JSONObject dataObj){
        /**
         * 1.拿到上一期的结束日期，加一天00.00，若没有则从1日00.00开始
         * 每个月的开始日期，就是上一期的结束日期加一天的00.00开始
         */
        int year = dataObj.getInteger("fiscalYear");
        String con1 = "type='logic' and className='AccountPeriod' and fiscalYear="+(year)+" and fiscalMonth=12";
        List<JSONObject> yearIns = this.logicInstanceDslService.query(con1,AccountPeriodClassName);
        if(yearIns.size()>0){
            return JsonResult.error(500, "已存在年份为："+year+"的会计期，不可新增！");
        }
        String con = "type='logic' and className='AccountPeriod' and fiscalYear="+(year-1)+" and fiscalMonth=12";
        List<JSONObject> ins = this.logicInstanceDslService.query(con,AccountPeriodClassName);
        List<JSONObject> dateList = new ArrayList<>();
        if("null".equals(dataObj.get("deadline").toString())||"".equals(dataObj.get("deadline"))){
            dateList = getFiscalPeriod(year);
        }else{
            int day = dataObj.getInteger("deadline");
            dateList = getFiscalPeriod(year,day);
        }
        if(ins.size()>0){
            try {
                JSONObject values = ins.get(0).getJSONObject("values");
                String endTime = values.getString("endTime");
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(format.parse(endTime));
                calendar.add(Calendar.DATE,1);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);

                dateList.get(0).put("beginTime",format.format(calendar.getTime()));
            }catch (Exception ex) {
                throw new BusinessException("999999", "会计期日期转换失败！", ex);
            }
        }
        List<JSONObject> insJsonList = dateList.stream().map(item->{
            JSONObject data = new JSONObject();
            data.put("type","logic");
            data.put("className",AccountPeriodClassName);
            data.put("values",item);
            return data;
        } ).collect(Collectors.toList());
        List<JSONObject> insList = this.logicInstanceDslService.batchAdd(insJsonList);
        return JsonResult.success(insList);
    }
    /**
     * 会计期启用接口
     *
     * @return 返回新增会计期的数据
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultInfo<?> enableFiscalPeriod(JSONObject dataObj){
        /**
         * 如果没有业务单据，几月份都行，也可以随意变更；有业务单据时，只能启用时间最早单据所在年月
         * 单据来源是销售订单和试制订单，订单的className
         * 更新会计期，更新已启用的状态为未启用
         */
        String con = "type='logic' and className='saleOrder' and date<='"+dataObj.getString("beginTime")+"'";
        PageInfo pageInfo = this.logicInstanceDslService.queryByPage(1,1,con,"saleOrder","", false);
        if(pageInfo.getRecords().size()>0){
            JSONObject json = (JSONObject) pageInfo.getRecords().get(0);
            String date = json.getJSONObject("values").getString("date");
            return JsonResult.error(500, "业务数据最早日期是："+date+",不可启用");
        }
        String updCon = "type='logic' and className='AccountPeriod' and status=true";
        JSONObject updData = new JSONObject();
        JSONObject values = new JSONObject();
        values.put("status",false);
        updData.put("values",values);
        this.logicInstanceDslService.updateByCondition(AccountPeriodClassName,updCon,updData);
        JSONObject updateData = new JSONObject();
        updateData.put("id",dataObj.getInteger("id"));
        values.put("status",true);
        updateData.put("values",values);
        JSONObject data= this.logicInstanceDslService.updateById(updateData);
        return JsonResult.success(data);
    }
    /**
     * 根据年份每个月的开始和结束时间, yyyy-MM-dd HH:mm:ss格式
     *
     * @return
     */
    public static List<JSONObject> getFiscalPeriod(int year) {
        List<JSONObject> dateList = new ArrayList<>();
        for(int month=1;month<13;month++){
            //获取月份的开始时间和结束时间
            // 获取当前分区的日历信息(这里可以使用参数指定时区)
            Calendar calendar = Calendar.getInstance();
            // 设置年
            calendar.set(Calendar.YEAR, year);
            // 设置月，月份从0开始
            calendar.set(Calendar.MONTH, month - 1);
            // 设置为指定月的第一天
            calendar.set(Calendar.DAY_OF_MONTH, 1);
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            // 获取指定月第一天的时间
            Date start = calendar.getTime();
            // 设置日历天数为当前月实际天数的最大值，即指定月份的最后一天
            calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
            // 获取最后一天的时间
            calendar.set(Calendar.HOUR_OF_DAY, 23);
            calendar.set(Calendar.MINUTE, 59);
            calendar.set(Calendar.SECOND, 59);
            Date end = calendar.getTime();
            JSONObject dateJson = new JSONObject();
            dateJson.put("fiscalYear",year);
            dateJson.put("fiscalMonth",month);
            dateJson.put("beginTime",format.format(start));
            dateJson.put("endTime",format.format(end));
            dateList.add(dateJson);
        }
        return dateList;
    }
    /**
     * 根据年份和截止日期，每个月的开始和结束时间, yyyy-MM-dd HH:mm:ss格式
     *
     * @return
     */
    public static List<JSONObject> getFiscalPeriod(int year,int day) {
        List<JSONObject> dateList = new ArrayList<>();
        for(int month=1;month<13;month++){
            //获取月份的开始时间和结束时间
            // 获取当前分区的日历信息(这里可以使用参数指定时区)
            Calendar calendar = Calendar.getInstance();
            if(1==month){
                // 设置年
                calendar.set(Calendar.YEAR, year-1);
                // 设置月，月份从0开始
                calendar.set(Calendar.MONTH, 11);
            }else{
                // 设置年
                calendar.set(Calendar.YEAR, year);
                // 设置月，月份从0开始
                calendar.set(Calendar.MONTH, month - 2);
            }
            // 设置为指定月的第一天
            calendar.set(Calendar.DAY_OF_MONTH, day);
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.add(Calendar.DATE,1);
            // 获取指定月第一天的时间
            Date start = calendar.getTime();
            // 设置日历天数为当前月实际天数的最大值，即指定月份的最后一天
            calendar.set(Calendar.YEAR, year);
            calendar.set(Calendar.MONTH, month - 1);
            calendar.set(Calendar.DATE, day);
            // 获取最后一天的时间
            calendar.set(Calendar.HOUR_OF_DAY, 23);
            calendar.set(Calendar.MINUTE, 59);
            calendar.set(Calendar.SECOND, 59);
            Date end = calendar.getTime();
            JSONObject dateJson = new JSONObject();
            dateJson.put("fiscalYear",year);
            dateJson.put("fiscalMonth",month);
            dateJson.put("beginTime",format.format(start));
            dateJson.put("endTime",format.format(end));
            dateList.add(dateJson);
        }
        return dateList;
    }
    /**
     * 计提折旧接口
     * 参数：折旧编码 code 折旧月份 yearMonth
     * @return 返回新增计提折旧的数据
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultInfo<?> provisionDepreciation(JSONObject dataObj){
        /**
         * 1.拿到到折旧月份，这就月份减一个月 查询开始计提时间在这个月月初之后，清理计提时间这个月月末之前的数据
         *
         */
        String yearMonth = dataObj.getString("yearMonth");
        String[] yearMonths = yearMonth.split("-");
        int year = Integer.valueOf(yearMonths[0]);
        int month = Integer.valueOf(yearMonths[1]);
        if(month==1){
            year = year - 1;
            month = 12;
        }else{
            month = month -1;
        }
        Calendar calendar = Calendar.getInstance();
        // 设置年
        calendar.set(Calendar.YEAR, year);
        // 设置月，月份从0开始
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DATE,1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        // 获取指定月第一天的时间
        Date start = calendar.getTime();
        // 设置日历天数为当前月实际天数的最大值，即指定月份的最后一天
        calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
        // 获取最后一天的时间
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        Date end = calendar.getTime();
        String con1 = "type='logic' and className='EquipmentInfo' and startAccrualTime<='"+format.format(start)+"' and (clearAssetsTime>='"+format.format(end)+"' or clearAssetsTime is null)";
        List<JSONObject> equIns = this.logicInstanceDslService.query(con1,"EquipmentInfo");
        String con2 = "type='logic' and className='EquipBetaInfo' and startAccrualTime<='"+format.format(start)+"' and (clearAssetsTime>='"+format.format(end)+"' or clearAssetsTime is null)";
        List<JSONObject> equTestIns = this.logicInstanceDslService.query(con2,"EquipmentInfo");
        equIns.addAll(equTestIns);
        int finalYear = year;
        int finalMonth = month;
        List<JSONObject> hierArr = equIns.stream().map(ins->{
            JSONObject json = new JSONObject();
            JSONObject hierValues = new JSONObject();
            hierValues.put("equId",ins.getInteger("id"));//设备Id
            hierValues.put("equCode",ins.getJSONObject("values").getString("code"));//设备编码
            hierValues.put("assetsCode",ins.getJSONObject("values").getString("assetsCode"));//资产编码
            /*计算使用月份开始*/
            String entryDate = ins.getJSONObject("values").getString("entryDate");//入场日期
            String[] entryDates = entryDate.split("-");
            int entryYear = Integer.valueOf(entryDates[0]);
            int entryMonth = Integer.valueOf(entryDates[1]);
            int usedMonth = finalYear *12+ finalMonth +1-entryYear*12-entryMonth;
            /*计算使用月份结束*/
            hierValues.put("entryDate",entryDate);
            int originalValue = ins.getJSONObject("values").getInteger("originalValue");
            hierValues.put("originalValue",originalValue);//原值
            int preSalvageValue = ins.getJSONObject("values").getInteger("preSalvageValue");
            hierValues.put("preSalvageValue",preSalvageValue);//预计净残值
            hierValues.put("usedMonth",usedMonth);//已使用月份
            /*计算本月折旧值开始 （原值-预计净残值）/(预计使用年限*12)*/
            BigDecimal depreciationYearDec= new BigDecimal(ins.getJSONObject("values").getInteger("depreciationYear").toString());
            BigDecimal originalValueDec= new BigDecimal(originalValue);
            BigDecimal preSalvageValueDec= new BigDecimal(preSalvageValue);
            BigDecimal depreciationMonthDec = originalValueDec.subtract(preSalvageValueDec).divide(depreciationYearDec.multiply(new BigDecimal(12)),2,BigDecimal.ROUND_HALF_UP);
            /*计算本月折旧值结束*/
            hierValues.put("depreciationMonth",depreciationMonthDec);//本月折旧值
            /*计算累计折旧值开始 本月折旧值*已使用月份*/
            BigDecimal accumulateDepreciationDecimal = depreciationMonthDec.multiply(new BigDecimal(usedMonth)).setScale(2, RoundingMode.HALF_UP);
            /*计算累计折旧值结束*/
            hierValues.put("accumulateDepreciation",accumulateDepreciationDecimal);//累计折旧值
            /*计算残值开始 原值-累计折旧值*/
            BigDecimal residualValueDecimal = originalValueDec.subtract(accumulateDepreciationDecimal).setScale(2, RoundingMode.HALF_UP);;
            /*计算残值结束*/
            hierValues.put("residualValue",residualValueDecimal);//残值
            json.put("hierValues",hierValues);
            return json;
        }).collect(Collectors.toList());
        BigDecimal totalAmount = hierArr.stream().map(item->{
            return item.getJSONObject("hierValues").getBigDecimal("depreciationMonth");
        }).reduce(BigDecimal.ZERO,BigDecimal::add);
        //删除传入年月的计提折旧
        this.logicInstanceDslService.deleteByCondition("Depreciation","type='logic' and className='Depreciation' and yearMonth='"+yearMonth+"'");
        JSONObject addData = new JSONObject();
        JSONObject values = dataObj;
        addData.put("type","logic");
        addData.put("className","Depreciation");
        values.put("totalAmount",totalAmount);
        addData.put("values",values);
        JSONObject addIns = this.logicInstanceDslService.add(addData);
        JSONObject addHier = new JSONObject();
        addHier.put("model","Logic");
        addHier.put("containerId",addIns.getInteger("id"));
        addHier.put("parentNodeId","jrjv1qi9zzk00");
        addHier.put("dataArray",hierArr);
        List<HierInstance> addHierIns =  this.hierInstanceDslService.addNodeAndLinkInstanceToListContainer(addHier);
        return JsonResult.success(addHierIns);
    }
    /**
     * 成本分摊接口
     * 参数：分摊月份 yearMonth
     * 参数：编码 code
     * @return 返回新增成本分摊的数据
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultInfo<?> costAllocation(JSONObject dataObj){

        /**
         * 获取拆分和组合bom数据
         *先获取出当前会计年月，拿到当前会计年月的开始和结束时间
         * 通过开始和结束时间拿到时间内的报工数据,根据背番计算产出量，倒扣量
         * 拿到ERP单价
         *
         */
        String yearMonth = dataObj.getString("yearMonth");
        String[] yearMonths = yearMonth.split("-");
        Integer year = Integer.valueOf(yearMonths[0]);
        Integer month = Integer.valueOf(yearMonths[1]);
        String con1 = "type='logic' and className='AccountPeriod' and fiscalYear="+year+" and fiscalMonth="+month;
        List<JSONObject> yearIns = this.logicInstanceDslService.query(con1,AccountPeriodClassName);
        if(yearIns.size()==0){
            return JsonResult.error(506,"不存在启用的会计期，请检查！");
        }
        /*Integer year = yearIns.get(0).getJSONObject("values").getInteger("fiscalYear");
        Integer month = yearIns.get(0).getJSONObject("values").getInteger("fiscalMonth");
        String yearMonth = year+"-"+month;*/
        String beginTime = yearIns.get(0).getJSONObject("values").getString("beginTime");
        String endTime = yearIns.get(0).getJSONObject("values").getString("endTime");
        //获取所有人员表
        List<JSONObject> salaryList = this.initSalaryList(yearMonth);
        Map<Integer,JSONObject> salaryMap = salaryList.stream().collect(Collectors.toMap(dto -> dto.getInteger("id"),dto->dto));
        //获取设备折旧数据
        List<JSONObject> equDepreciationList = this.initDepreciationList(yearMonth);
        Map<Integer,JSONObject> equMap = equDepreciationList.stream().collect(Collectors.toMap(dto -> dto.getInteger("id"),dto->dto));
        //获取模具费数据
        List<JSONObject> mouldCostList = this.initMouldList(yearMonth);
        Map<String,JSONObject> mouldCostMap = mouldCostList.stream().collect(Collectors.toMap(dto -> dto.getString("code"),dto->dto));
        //获取费用录入数据
        JSONObject costValue = this.initCostData(yearMonth);
        //获取月结表数据，拿到上月期初值
        List<JSONObject> startList = this.getMonthBalanceValue(yearMonth);
        Map<String,List<JSONObject>> startMap = startList.stream().collect(Collectors.groupingBy(dto -> dto.getString("bcode")));
        //查询库存数据
        List<JSONObject> stockDetail = this.queryStockDetail(beginTime,endTime);
        Map<String,List<JSONObject>> stockDetailMap = stockDetail.stream().collect(Collectors.groupingBy(dto -> dto.getString("material_bcode")));
        //查询ERP单价数据
        List<JSONObject> priceList = this.initERPPriceData(yearMonth);
        //查询研发与非研发比例
        List<JSONObject> ratioList = initRatio(beginTime,endTime);
        //获取废料表数据
        List<JSONObject> wasteList = this.initWasteData(yearMonth);
        //根据背番计算
        List<JSONObject> reportList = new ArrayList<>();
        //人工工时及产量
        List<JSONObject> laborList = new ArrayList<>();
        Map<String,List<JSONObject>> laborMap = new HashMap<>();
        //机器工时
        List<JSONObject> equipmentList = new ArrayList<>();
        Map<String,List<JSONObject>> equipmentMap = new HashMap<>();
        //模具工时
        List<JSONObject> mouldList = new ArrayList<>();
        Map<String,List<JSONObject>> mouldMap = new HashMap<>();
        //倒扣库存
        List<JSONObject> deductList = new ArrayList<>();
        this.initReportList(yearMonth,reportList,laborList,equipmentList,mouldList,laborMap,equipmentMap,mouldMap,deductList);
        Map<String,List<JSONObject>> deductListForBcode = deductList.stream().collect(Collectors.groupingBy(dto -> dto.getString("bcode")));
        //人工所有工时
        BigDecimal laborAllHour = laborList.stream().map(dto -> dto.getBigDecimal("hour"))
                .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        //机器所有工时
        BigDecimal equipmentAllHour = equipmentList.stream().map(dto -> dto.getBigDecimal("hour"))
                .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        //计算人的单价
        List<JSONObject> laborPriceList = this.computeLaborPrice(laborList,salaryMap);
        //单价转map，key是人的id
        Map<Integer, JSONObject> laborPriceMap = laborPriceList.stream().collect(Collectors.toMap(dto -> dto.getInteger("person"),dto->dto));

        //计算设备的单价
        List<JSONObject> equPriceList = this.computeEquipmentPrice(equipmentList,equMap);
        Map<Integer, JSONObject> equPriceMap = equPriceList.stream().collect(Collectors.toMap(dto -> dto.getInteger("equipment"),dto->dto));

        //计算模具的单价
        List<JSONObject> mouldPriceList = this.computeMouldPrice(mouldList,mouldCostMap);
        Map<String, JSONObject> mouldPriceMap = mouldPriceList.stream().collect(Collectors.toMap(dto -> dto.getString("mould"),dto->dto));
        //拿到模具的工时单价*设备的工时
        //拿到人的工时单价*人的工时
        //拿到设备的工时单价*设备的工时
        reportList.stream().forEach(reportJson->{
            String bcode = reportJson.getString("bcode");
            List<JSONObject> list = laborMap.get(bcode);
            //循环拿到人的单价和计件类型，价格相加]
            BigDecimal laborAll = new BigDecimal(0);
            BigDecimal laborAssistAll = new BigDecimal(0);
            //循环拿到设备的单价价格相加]
            BigDecimal laborEquAll = new BigDecimal(0);
            //循环拿到模具的单价价格相加]
            BigDecimal laborMouldAll = new BigDecimal(0);
            list.stream().forEach(item->{
                //计算人工费
                Integer operator = item.getInteger("operator");
                JSONObject laborPrice = laborPriceMap.get(operator);
                String attribute = laborPrice.getString("attribute");
                String quantityKey = "hour";
                String priceKey = "hourPrice";
                if("2".equals(laborPrice.getString("allocationWay"))){
                    quantityKey = "quantity";
                    priceKey = "quantityPrice";
                }
                if("1".equals(attribute)){
                    laborAll.add(item.getBigDecimal(quantityKey).multiply(laborPrice.getBigDecimal(priceKey)));
                }else{
                    laborAssistAll.add(item.getBigDecimal(quantityKey).multiply(laborPrice.getBigDecimal(priceKey)));
                }
                //计算设备折旧费
                if(item.get("equipment")==null){
                    laborEquAll.add(new BigDecimal(0));
                }else{
                    Integer equipment = item.getInteger("equipment");
                    JSONObject equPrice = equPriceMap.get(equipment);
                    laborEquAll.add(item.getBigDecimal("hour").multiply(equPrice.getBigDecimal("hourPrice")));
                }

                //计算模具费
                if(item.get("mould")==null){
                    laborMouldAll.add(new BigDecimal(0));
                }else {
                    String mould = item.getString("mould");
                    JSONObject mouldPrice = mouldPriceMap.get(mould);
                    laborMouldAll.add(item.getBigDecimal("hour").multiply(mouldPrice.getBigDecimal("hourPrice")));
                }
            });
            BigDecimal laborAllHourForPerson = list.stream().map(dto -> dto.getBigDecimal("hour"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);//此背番人员所有工时
            //获取期初值
            List<JSONObject> startValueList = startMap.get(bcode)==null?new ArrayList<>():startMap.get(bcode);
            //期初数量
            BigDecimal actualQuantity = startValueList.stream().filter(dto->dto.get("actualQuantity")!=null)
                    .map(dto -> dto.getBigDecimal("actualQuantity"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //期初金额
            BigDecimal actualAmount = startValueList.stream().filter(dto->dto.get("actualAmount")!=null)
                    .map(dto -> dto.getBigDecimal("actualAmount"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //被倒冲数量总和
            List<JSONObject> deductListAll = deductListForBcode.get(bcode)==null?new ArrayList<>():deductListForBcode.get(bcode);
            BigDecimal deductAll = deductListAll.stream().filter(dto->dto.get("quality")!=null)
                    .map(dto -> dto.getBigDecimal("quality"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //所有库存数据
            List<JSONObject> stockDetailList = stockDetailMap.get(bcode)==null?new ArrayList<>():stockDetailMap.get(bcode);
            //其他入库
            BigDecimal otherInQuality = stockDetailList.stream().filter(dto->"35".equals(dto.get("form_type"))&&dto.get("quantity")!=null)
                    .map(dto -> dto.getBigDecimal("quality"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //销售出库
            BigDecimal saleOutQuality = stockDetailList.stream().filter(dto->"22".equals(dto.get("form_type"))&&dto.get("quantity")!=null)
                    .map(dto -> dto.getBigDecimal("quality"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //销售补货
            BigDecimal salePatchQuality = stockDetailList.stream().filter(dto->"42".equals(dto.get("form_type"))&&dto.get("quantity")!=null)
                    .map(dto -> dto.getBigDecimal("quality"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //销售退货
            BigDecimal saleReturnQuality = stockDetailList.stream().filter(dto->"23".equals(dto.get("form_type"))&&dto.get("quantity")!=null)
                    .map(dto -> dto.getBigDecimal("quality"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //其他出库暂无
            //盘盈
            BigDecimal profitQuality = stockDetailList.stream().filter(dto->"14".equals(dto.get("form_type"))&&dto.get("quantity")!=null)
                    .map(dto -> dto.getBigDecimal("quality"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //盘亏
            BigDecimal lossQuality = stockDetailList.stream().filter(dto->"15".equals(dto.get("form_type"))&&dto.get("quantity")!=null)
                    .map(dto -> dto.getBigDecimal("quality"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //入数量：报工入库+其他入库
            BigDecimal inQuantity = otherInQuality.add(reportJson.getBigDecimal("reportNumber"));
            //出数量：倒冲数量总和+销售出库+销售补货-销售退货+其他出库+盘亏-盘盈
            BigDecimal outQuantity = deductAll.add(saleOutQuality).add(salePatchQuality).subtract(saleReturnQuality).add(lossQuality).subtract(profitQuality);
            //被倒冲数量总和
            reportJson.put("deductAll",deductAll);
            //其他入库
            reportJson.put("otherInQuality",otherInQuality);
            //销售出库
            reportJson.put("saleOutQuality",saleOutQuality);
            //销售补货
            reportJson.put("salePatchQuality",salePatchQuality);
            //销售退货
            reportJson.put("saleReturnQuality",saleReturnQuality);
            //盘盈
            reportJson.put("profitQuality",profitQuality);
            //盘亏
            reportJson.put("lossQuality",lossQuality);
            //期初数量
            reportJson.put("actualQuantity",actualQuantity);
            //期初金额
            reportJson.put("actualAmount",actualAmount);
            //入数量
            reportJson.put("inQuantity",inQuantity);
            //出数量
            reportJson.put("outQuantity",outQuantity);
            //修理费
            BigDecimal repairPrice = costValue.getBigDecimal("repairPrice").multiply(laborAllHourForPerson).divide(laborAllHour,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("repairPrice",repairPrice);
            //摊销费(元)
            BigDecimal amortizationPrice = costValue.getBigDecimal("amortizationPrice").multiply(laborAllHourForPerson).divide(laborAllHour,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("amortizationPrice",amortizationPrice);
            //消耗材料费(元)
            BigDecimal consumePrice = costValue.getBigDecimal("consumePrice").multiply(laborAllHourForPerson).divide(laborAllHour,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("consumePrice",consumePrice);
            //运输费(元)
            BigDecimal transportPrice = costValue.getBigDecimal("transportPrice").multiply(laborAllHourForPerson).divide(laborAllHour,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("transportPrice",transportPrice);
            //其他费用
            BigDecimal otherPrice = costValue.getBigDecimal("transportPrice").multiply(laborAllHourForPerson).divide(laborAllHour,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("otherPrice",otherPrice);
            //其他金额：修理费+摊销费+消耗材料费+运输费+其他费用
            BigDecimal otherCost = repairPrice.add(amortizationPrice).add(consumePrice).add(transportPrice).add(otherPrice);
            reportJson.put("otherCost",otherCost);
            //生产人员人工费
            reportJson.put("laborCost",laborAll);
            //辅助生产人员人工费
            reportJson.put("assistCost",laborAssistAll);
            //此背番设备所有工时
            BigDecimal laborAllHourForEquipment = list.stream().map(dto -> dto.getBigDecimal("hour"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //能源费(元)
            BigDecimal energyCost = costValue.getBigDecimal("energyPrice").multiply(laborAllHourForEquipment).divide(equipmentAllHour,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("energyCost",energyCost);
            //设备折旧费
            reportJson.put("depreciationCost",laborEquAll);
            //模具费
            reportJson.put("moldCost",laborMouldAll);
            BigDecimal buildCost = laborAssistAll.add(laborEquAll).add(energyCost).add(laborMouldAll).add(otherCost);
            //制造费：辅助生产人员人工费+折旧费+模具费+其他
            reportJson.put("buildCost",buildCost);
            //废料金额
            BigDecimal wasteAmount = wasteList.stream().filter(dto->bcode.equals(dto.getBigDecimal("bcode"))&&dto.get("wasteAmount")!=null)
                    .map(dto -> dto.getBigDecimal("wasteAmount"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //报废金额
            BigDecimal scrapAmount = wasteList.stream().filter(dto->bcode.equals(dto.getBigDecimal("bcode"))&&dto.get("scrapAmount")!=null)
                    .map(dto -> dto.getBigDecimal("scrapAmount"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            //废料：废料金额+报废金额
            BigDecimal wasteCost = wasteAmount.add(scrapAmount);
            reportJson.put("wasteCost",wasteCost);
            //计算单价
            JSONArray matArray = reportJson.getJSONArray("deduct");
            List<BigDecimal> amountList = new ArrayList<>();
            matArray.stream().forEach(material->{
                JSONObject materialJson = (JSONObject) JSONObject.toJSON(material);
                String materialBcode = materialJson.getString("bcode");
                List<JSONObject> priceListFilter = priceList.stream().filter(priceItem->materialBcode.equals(priceItem.getString("bcode"))).collect(Collectors.toList());
                BigDecimal price = new BigDecimal(0);
                if(priceListFilter.size()>0){
                    price = priceListFilter.get(0).getBigDecimal("price");
                }else{
                    this.getPrice(priceList,reportList,materialBcode);
                }
                BigDecimal amount = price.multiply(materialJson.getBigDecimal("quality"));
                materialJson.put("price",price);
                materialJson.put("amount",amount);
                amountList.add(amount);
            });
            BigDecimal amountInAll = amountList.stream().map(amount -> amount)
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal proInPrice = amountInAll.divide(inQuantity,2,BigDecimal.ROUND_HALF_UP);
            //入单价
            reportJson.put("inPrice",proInPrice);
            //入金额
            reportJson.put("inAmount",amountInAll);
            BigDecimal outPrice = (actualAmount.add(amountInAll)).divide(actualQuantity.add(inQuantity),2,BigDecimal.ROUND_HALF_UP);
            BigDecimal outAmount = outPrice.multiply(outQuantity);
            //出单价
            reportJson.put("outPrice",outPrice);
            //出金额
            reportJson.put("outAmount",outAmount);
            //材料费
            reportJson.put("materialCost",outAmount);
            //金额：材料费+人工费+制造费
            BigDecimal cost = outAmount.add(laborAll).add(buildCost);
            reportJson.put("cost",cost);
            //单价：金额/入数量
            BigDecimal price = cost.divide(inQuantity,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("price",price);
            JSONObject json5 = new JSONObject();
            json5.put("bcode",reportJson.getString("bcode"));
            json5.put("price",outPrice);
            priceList.add(json5);
        });
        //计算研发费
        List<JSONObject> trialCostList = new ArrayList<>();
        //计算研发费
        List<JSONObject> noTrialCostList = new ArrayList<>();
        reportList.stream().forEach(report->{
            String bcode = report.getString("bcode");
            List<JSONObject> listFilter = new ArrayList<>();
            if(ratioList.size()>0){
                listFilter = ratioList.stream().filter(dto->bcode.equals(dto.getString("bcode")))
                        .collect(Collectors.toList());
            }
            BigDecimal ratio = new BigDecimal(1);
            if(listFilter.size()>0) {
                BigDecimal trialRatio = listFilter.get(0).getBigDecimal("trialRatio");
                ratio = listFilter.get(0).getBigDecimal("ratio");
                JSONObject trialJson = new JSONObject();
                for (Map.Entry<String, Object> entry : report.entrySet()) {
                    String key = entry.getKey();
                    if ("bcode".equals(key)||"deduct".equals(key)||"pcode".equals(key)) {
                        trialJson.put(key, entry.getValue());
                    } else {
                        BigDecimal value = this.getBigDecimal(entry.getValue());
                        BigDecimal newValue = value.multiply(trialRatio);
                        trialJson.put(key, newValue);
                    }
                }
                trialCostList.add(trialJson);
            }
            JSONObject trialJson = new JSONObject();
            for (Map.Entry<String, Object> entry : report.entrySet()) {
                String key = entry.getKey();
                if ("bcode".equals(key)||"deduct".equals(key)||"pcode".equals(key)) {
                    trialJson.put(key, entry.getValue());
                } else {
                    BigDecimal value = this.getBigDecimal(entry.getValue());
                    BigDecimal newValue = value.multiply(ratio);
                    trialJson.put(key, newValue);
                }
            }
            noTrialCostList.add(trialJson);
        });

        //删除启用会计期的成本分摊表
        this.logicInstanceDslService.deleteByCondition("CostAllocation","type='logic' and className='CostAllocation' and yearMonth='"+yearMonth+"'");
        //新增研发实例
        JSONObject addData = new JSONObject();
        JSONObject values = dataObj;
        addData.put("type","logic");
        addData.put("className","CostAllocation");
        values.put("costType","1");
        addData.put("values",values);
        JSONObject addIns = this.logicInstanceDslService.add(addData);
        List<JSONObject> hierArr = trialCostList.stream().map(report->{
            JSONObject hier = new JSONObject();
            hier.put("hierValues",report);
            return hier;
        }).collect(Collectors.toList());
        JSONObject addHier = new JSONObject();
        addHier.put("model","Logic");
        addHier.put("containerId",addIns.getInteger("id"));
        addHier.put("parentNodeId","4swt9mt6jdi000");
        addHier.put("dataArray",hierArr);
        List<HierInstance> addHierIns =  this.hierInstanceDslService.addNodeAndLinkInstanceToListContainer(addHier);
        //新增非研发实例
        JSONObject addData2 = new JSONObject();
        JSONObject values2 = dataObj;
        addData2.put("type","logic");
        addData2.put("className","CostAllocation");
        values2.put("costType","0");
        addData2.put("values",values2);
        JSONObject addIns2 = this.logicInstanceDslService.add(addData2);
        List<JSONObject> hierArr2 = noTrialCostList.stream().map(report->{
            JSONObject hier = new JSONObject();
            hier.put("hierValues",report);
            return hier;
        }).collect(Collectors.toList());
        JSONObject addHier2 = new JSONObject();
        addHier2.put("model","Logic");
        addHier2.put("containerId",addIns2.getInteger("id"));
        addHier2.put("parentNodeId","4swt9mt6jdi000");
        addHier2.put("dataArray",hierArr2);
        List<HierInstance> addHierIns2 =  this.hierInstanceDslService.addNodeAndLinkInstanceToListContainer(addHier2);
        return JsonResult.success(addHierIns2);
    }

    /**
     * Object转BigDecimal
     * @param value
     * @return
     */
    private  BigDecimal getBigDecimal(Object value) {
        BigDecimal ret = null;
        if (value != null) {
            if (value instanceof BigDecimal) {
                ret = (BigDecimal) value;
            } else if (value instanceof String) {
                ret = new BigDecimal((String) value);
            } else if (value instanceof BigInteger) {
                ret = new BigDecimal((BigInteger) value);
            } else if (value instanceof Number) {
                ret = new BigDecimal(((Number) value).doubleValue());
            } else {
                throw new ClassCastException("Not possible to coerce [" + value + "] from class " + value.getClass() + " into a BigDecimal.");
            }
        }
        return ret;
    }
    public ResultInfo<?> computePrice(JSONObject dataObj){
        List<JSONObject> priceList = new ArrayList<>();
        JSONObject json1 = new JSONObject();
        json1.put("bcode","A1");
        json1.put("price",1);
        priceList.add(json1);
        JSONObject json2 = new JSONObject();
        json2.put("bcode","A3");
        json2.put("price",5);
        priceList.add(json2);
        JSONObject json3 = new JSONObject();
        json3.put("bcode","B");
        json3.put("price",10);
        priceList.add(json3);
        JSONObject json4 = new JSONObject();
        json4.put("bcode","G002");
        json4.put("price",8);
        priceList.add(json4);
        JSONArray reportArray = dataObj.getJSONArray("data");
        List<JSONObject> reportList = new ArrayList<>();
        reportArray.stream().forEach(report->{
            JSONObject reportJson = (JSONObject) JSONObject.toJSON(report);
            reportList.add(reportJson);
        });
        for(int i=0;i<reportList.size();i++){
            JSONObject reportJson = reportList.get(i);
            //入数量：报工入库+其他入库
            BigDecimal inQuantity = reportJson.getBigDecimal("inQuantity");
            JSONArray matArray = reportJson.getJSONArray("deduct");
            List<BigDecimal> amountList = new ArrayList<>();
            matArray.stream().forEach(material->{
                JSONObject materialJson = (JSONObject) JSONObject.toJSON(material);
                String bcode = materialJson.getString("bcode");
                List<JSONObject> priceListFilter = priceList.stream().filter(priceItem->bcode.equals(priceItem.getString("bcode"))).collect(Collectors.toList());
                BigDecimal price = new BigDecimal(0);
                if(priceListFilter.size()>0){
                    price = priceListFilter.get(0).getBigDecimal("price");
                }else{
                    this.getPrice(priceList,reportList,bcode);
                }
                BigDecimal amount = price.multiply(materialJson.getBigDecimal("quality"));
                materialJson.put("price",price);
                materialJson.put("amount",amount);
                amountList.add(amount);
            });
            BigDecimal amountAll = amountList.stream().map(amount -> amount)
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal proPrice = amountAll.divide(inQuantity,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("price",proPrice);
            reportJson.put("amount",amountAll);
            JSONObject json5 = new JSONObject();
            json5.put("bcode",reportJson.getString("bcode"));
            json5.put("price",proPrice);
            priceList.add(json5);
        }
        return JsonResult.success(reportList);
    }
    public List<JSONObject> computePrice(List<JSONObject> priceList,List<JSONObject> reportList){

        for(int i=0;i<reportList.size();i++){
            JSONObject reportJson = reportList.get(i);
            //入数量：报工入库+其他入库
            BigDecimal inQuantity = reportJson.getBigDecimal("inQuantity");
            JSONArray matArray = reportJson.getJSONArray("deduct");
            List<BigDecimal> amountList = new ArrayList<>();
            matArray.stream().forEach(material->{
                JSONObject materialJson = (JSONObject) JSONObject.toJSON(material);
                String bcode = materialJson.getString("bcode");
                List<JSONObject> priceListFilter = priceList.stream().filter(priceItem->bcode.equals(priceItem.getString("bcode"))).collect(Collectors.toList());
                BigDecimal price = new BigDecimal(0);
                if(priceListFilter.size()>0){
                    price = priceListFilter.get(0).getBigDecimal("price");
                }else{
                    this.getPrice(priceList,reportList,bcode);
                }
                BigDecimal amount = price.multiply(materialJson.getBigDecimal("quality"));
                materialJson.put("price",price);
                materialJson.put("amount",amount);
                amountList.add(amount);
            });
            BigDecimal amountAll = amountList.stream().map(amount -> amount)
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal proPrice = amountAll.divide(inQuantity,2,BigDecimal.ROUND_HALF_UP);
            reportJson.put("price",proPrice);
            reportJson.put("amount",amountAll);
            JSONObject json5 = new JSONObject();
            json5.put("bcode",reportJson.getString("bcode"));
            json5.put("price",proPrice);
            priceList.add(json5);
        }
        return reportList;
    }
    private BigDecimal getPrice(List<JSONObject> priceList, List<JSONObject> reportList,String proBcode){
        /*List<JSONObject> reportListFilter = reportList.stream().filter(report->
            proBcode.equals(report.getString("bcode"))
        ).collect(Collectors.toList());
        if(reportListFilter.size()==0){
            throw new BusinessException(506, proBcode+"找不到单价！");
        }*/
        BigDecimal proInPrice = new BigDecimal(0);

        for(int i=0;i<reportList.size();i++){
            JSONObject reportJson = reportList.get(i);
            if(proBcode.equals(reportJson.getString("bcode"))) {
                //入数量：报工入库+其他入库
                BigDecimal inQuantity = reportJson.getBigDecimal("inQuantity");
                JSONArray matArray = reportJson.getJSONArray("deduct");
                List<BigDecimal> amountList = new ArrayList<>();
                matArray.stream().forEach(material -> {
                    JSONObject materialJson = (JSONObject) JSONObject.toJSON(material);
                    String bcode = materialJson.getString("bcode");
                    List<JSONObject> priceListFilter = priceList.stream().filter(priceItem -> bcode.equals(priceItem.getString("bcode"))).collect(Collectors.toList());
                    BigDecimal price = new BigDecimal(0);
                    if (priceListFilter.size() > 0) {
                        price = priceListFilter.get(0).getBigDecimal("price");
                    } else {
                        this.getPrice(priceList, reportList, bcode);
                    }
                    BigDecimal amount = price.multiply(materialJson.getBigDecimal("quality"));
                    materialJson.put("price", price);
                    materialJson.put("amount", amount);
                    amountList.add(amount);
                });
                BigDecimal amountInAll = amountList.stream().map(amount -> amount)
                        .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                proInPrice = amountInAll.divide(inQuantity,2,BigDecimal.ROUND_HALF_UP);
                //入单价
                reportJson.put("inPrice",proInPrice);
                //入金额
                reportJson.put("inAmount",amountInAll);
                //获取期初单价，期初数量，出数量
                BigDecimal actualAmount = reportJson.getBigDecimal("actualAmount");
                BigDecimal actualQuantity = reportJson.getBigDecimal("actualQuantity");
                BigDecimal outQuantity = reportJson.getBigDecimal("outQuantity");
                BigDecimal laborCost = reportJson.getBigDecimal("laborCost");
                BigDecimal buildCost = reportJson.getBigDecimal("buildCost");
                BigDecimal outPrice = (actualAmount.add(amountInAll)).divide(actualQuantity.add(inQuantity),2,BigDecimal.ROUND_HALF_UP);
                BigDecimal outAmount = outPrice.multiply(outQuantity);
                //出单价
                reportJson.put("outPrice",outPrice);
                //出金额
                reportJson.put("outAmount",outAmount);
                //材料费
                reportJson.put("materialCost",outAmount);
                //金额：材料费+人工费+制造费
                BigDecimal cost = outAmount.add(laborCost).add(buildCost);
                reportJson.put("cost",cost);
                //单价：金额/入数量
                BigDecimal price = cost.divide(inQuantity,2,BigDecimal.ROUND_HALF_UP);
                reportJson.put("price",price);
                JSONObject json5 = new JSONObject();
                json5.put("bcode", reportJson.getString("bcode"));
                json5.put("price", proInPrice);
                priceList.add(json5);
            }
        }
        return proInPrice;
    }
    private List<JSONObject>  getMonthBalanceValue(String yearMonth){
        List<JSONObject> startList = new ArrayList<>();
        String[] yearMonths = yearMonth.split("-");
        int year = Integer.valueOf(yearMonths[0]);
        int month = Integer.valueOf(yearMonths[1]);
        if(month==1){
            year = year - 1;
            month = 12;
        }else{
            month = month -1;
        }
        String preYearMonth = year+"-"+month;
        List<JSONObject> monthBalanceIns= this.logicInstanceDslService.query("type = 'logic' and className = 'MonthBalance' and month = '"+preYearMonth+"'","MonthBalance");
        if(monthBalanceIns.size()==0){
            throw new BusinessException(500, yearMonth+"月结表不存在！");
        }
        //根据背番号分组，计算出背番号的期末数量及金额
        Map<String,List<JSONObject>> monthBalanceForBcode = monthBalanceIns.stream().collect(Collectors.groupingBy(dto -> dto.getJSONObject("values").getString("bcode")));
        for (Map.Entry<String, List<JSONObject>> entry : monthBalanceForBcode.entrySet()) {
            String bcode = entry.getKey();
            List<JSONObject> list = monthBalanceForBcode.get(bcode);
            BigDecimal actualQuantityAll = list.stream().filter(item -> item.getInteger("actualQuantity") != null)
                    .map(dto -> dto.getBigDecimal("actualQuantity"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal actualAmountAll = list.stream().filter(item -> item.getInteger("actualAmount") != null)
                    .map(dto -> dto.getBigDecimal("actualAmount"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            JSONObject json = new JSONObject();
            json.put("bcode",bcode);
            json.put("actualQuantityAll",actualQuantityAll);
            json.put("actualAmountAll",actualAmountAll);
            startList.add(json);
        }
        return startList;
    }
    private List<ProductBomMaster> initCombineBomList(){
        List<ProductBomMaster> combinedBomList = new ArrayList<>();
        StaticLogicInstance staticLogicInstance = staticLogicInstanceDslService.fetchByClassName("CombinedBOM");
        List<HierInstance> combinedHierInstances = this.hierInstanceDslService.queryByContainerId(ModelType.StaticLogic.getCode(), staticLogicInstance.getClassName(), staticLogicInstance.getId());
        this.buildProductBom(combinedHierInstances, combinedBomList);
        return combinedBomList;
    }
    private List<ProductBomMaster> initSplitBomList(){
        List<ProductBomMaster> splitBomList = new ArrayList<>();
        StaticLogicInstance staticLogicInstance = staticLogicInstanceDslService.fetchByClassName("SplitBOM");
        List<HierInstance> splitHierInstances = this.hierInstanceDslService.queryByContainerId(ModelType.StaticLogic.getCode(), staticLogicInstance.getClassName(), staticLogicInstance.getId());
        this.buildProductBom(splitHierInstances, splitBomList);
        return splitBomList;
    }
    private void buildProductBom(List<HierInstance> bomHierInstances, List<ProductBomMaster> pBomList) {
        List<HierInstance> hierInsList = bomHierInstances.stream().filter(d -> "root".equalsIgnoreCase(d.getParentId())).collect(Collectors.toList());
        hierInsList.forEach(hIns -> {
            JSONObject values = hIns.getValues();
            Boolean enable = values.getBoolean("enable");
            if (enable) {
                String nodeId = hIns.getNodeId();
                String parentId = hIns.getParentId();
                String bomCode = values.getString("bomCode");
                String materialPCode = values.getString("material_pcode");
                String materialBCode = values.getString("material_bcode");
                String materialName = values.getString("material_name");
                String materialUnit = values.getString("material_unit");
                Integer invertWarehouse = values.getInteger("invertWarehouse");
                Integer invertLocation = values.getInteger("invertLocation");
                Double quantity = values.getDouble("quantity");
                Double lossRate = values.getDouble("lossRate");
                Double bomVersion = values.getDouble("bomVersion");
                ProductBomMaster productBomMaster = new ProductBomMaster();
                productBomMaster.setBomCode(bomCode);
                productBomMaster.setMaterialPCode(materialPCode);
                productBomMaster.setMaterialBCode(materialBCode);
                productBomMaster.setMaterialName(materialName);
                productBomMaster.setMaterialUnit(materialUnit);
                productBomMaster.setInvertWarehouse(invertWarehouse);
                productBomMaster.setInvertLocation(invertLocation);
                productBomMaster.setEnable(true);
                productBomMaster.setQuantity(quantity);
                productBomMaster.setLossRate(lossRate == null ? 0.0 : lossRate/100);
                bomVersion = bomVersion == null ? 1.0 : bomVersion;
                productBomMaster.setBomVersion(bomVersion);
                productBomMaster.setNodeId(nodeId);
                productBomMaster.setParentId(parentId);
                List<ProductBomSub> productBomSubs = this.buildProductBomSub(bomHierInstances, nodeId);
                productBomMaster.setProductBomSubList(productBomSubs);
                pBomList.add(productBomMaster);
            }
        });
    }
    private List<ProductBomSub> buildProductBomSub(List<HierInstance> bomHierInstances, String parentId) {
        List<HierInstance> hierInsList = bomHierInstances.stream().filter(d -> d.getParentId().equalsIgnoreCase(parentId)).collect(Collectors.toList());
        List<ProductBomSub> productBomSubs = new ArrayList<>();
        hierInsList.forEach(hIns -> {
            JSONObject values = hIns.getValues();
            String nodeId = hIns.getNodeId();
            String materialBCode = values.getString("material_bcode");
            String materialPCode = values.getString("material_pcode");
            String materialName = values.getString("material_name");
            String materialUnit = values.getString("material_unit");
            Integer invertWarehouse = values.getInteger("invertWarehouse");
            Integer invertLocation = values.getInteger("invertLocation");
            Double quantity = values.getDouble("quantity");
            Double lossRate = values.getDouble("lossRate");
            String bomCode = values.getString("bomCode");
            String splitBomCode = values.getString("split_bomCode");
            ProductBomSub productBomSub = new ProductBomSub();
            productBomSub.setBomCode(bomCode);
            productBomSub.setMaterialPCode(materialPCode);
            productBomSub.setMaterialBCode(materialBCode);
            productBomSub.setMaterialName(materialName);
            productBomSub.setMaterialUnit(materialUnit);
            productBomSub.setInvertWarehouse(invertWarehouse);
            productBomSub.setInvertLocation(invertLocation);
            productBomSub.setQuantity(quantity);
            productBomSub.setLossRate(lossRate == null ? 0.0 : lossRate/100);
            productBomSub.setNodeId(nodeId);
            productBomSub.setParentId(parentId);
            productBomSub.setSplitBomCode(splitBomCode);
            productBomSubs.add(productBomSub);
        });
        return productBomSubs;
    }

    /**
     * 月末结转接口
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultInfo<?> carryForward(){
        Calendar calendar = Calendar.getInstance();
        // 获取当前年
        Integer year = calendar.get(Calendar.YEAR);
//      获取当前月
        Integer month = calendar.get(Calendar.MONTH) + 1;
//        Integer year = dataObj.getInteger("year");
//        Integer month = dataObj.getInteger("month");
        /**
         * 检查会计期状态
         * 是否存在盘点单
         * 是否存在“已创建”且“未审核”单据
         * 是否生已获取成本单价（ERP接口）
         * 是否完成当月折旧
         * 是否完成当月费用录入
         * 是否完成废料录入
         * 是否已结算工资
         * 是否已进行成本分摊
         * 生成结转单（表单）
         * 将会计期设置为下一个月
         * 完成月末结转
         */
        //检查会计期状态
        String beginTime = "";
        String endTime = "";
        List<JSONObject> processList = new ArrayList<>();
        List<JSONObject> reasonList = new ArrayList<>();
        String con1 = "type='logic' and className='AccountPeriod' and status=true";
        List<JSONObject> yearIns = this.logicInstanceDslService.query(con1,AccountPeriodClassName);
        Integer status1 = 1;
        if(yearIns.size()==0){
            status1 = 0;
            JSONObject reason1 = new JSONObject();
            reason1.put("step",1);
            reason1.put("docType","会计期");
            reason1.put("docStatus","此年月不存在启用会计期");
            reason1.put("count",1);
            reasonList.add(reason1);
        }else if(yearIns.size()>1){
            status1 = 0;
            JSONObject reason1 = new JSONObject();
            reason1.put("step",1);
            reason1.put("docType","会计期");
            reason1.put("docStatus","存在多个启用会计期");
            reason1.put("count",1);
            reasonList.add(reason1);
        }else{
            year = yearIns.get(0).getJSONObject("values").getInteger("fiscalYear");
            month = yearIns.get(0).getJSONObject("values").getInteger("fiscalMonth");
            beginTime = yearIns.get(0).getJSONObject("values").getString("beginTime");
            endTime = yearIns.get(0).getJSONObject("values").getString("endTime");
        }
        JSONObject process1 = new JSONObject();
        process1.put("step",1);
        process1.put("status",status1);
        processList.add(process1);
        //是否存在盘点单
        String con2 = "type = 'logic' and className = 'takeStockOrder' and operateDate like '"+year+"-"+month+"%'";
        List<JSONObject> inventoryIns = this.logicInstanceDslService.query(con2,"takeStockOrder");
        Integer status2 = 1;
        if(inventoryIns.size()==0){
            status2 = 0;
            JSONObject reason2 = new JSONObject();
            reason2.put("step",2);
            reason2.put("docType","盘点单");
            reason2.put("docStatus","此年月不存在盘点单");
            reason2.put("count",1);
            reasonList.add(reason2);
        }
        JSONObject process2 = new JSONObject();
        process2.put("step",2);
        process2.put("status",status2);
        processList.add(process2);
        //是否存在“已创建”且“未审核”单据
        /**
         * workOrder  status:finish
         * materialRecord approvalStatus:true
         * materialReturn approvalStatus:true
         * workReport status:finish
         * repairWorkOrder approvalStatus:true
         * repairWorkReport reportFlag:true
         * workQuality approvalStatus:true
         * returnQualityNotice approvalStatus:true
         * workQualityDelivery approvalStatus:true
         * PurchasePlan, 采购计划
         * PurchaseOrder, 采购订单
         * PurchaseReturn, 采购/委外退货单
         * PurchaseReplenish, 采购/委外补货单
         * receiptOrder, 采购/委外入库单
         * saleOrder,销售订单
         * saleOutBound,销售出库单
         * saleReturn,销售退货入库单
         * saleReturnWorkOrder,销售退货返修单
         * saleReplenish,销售补货单
         * returnQualityNotice,销售退货返修质检
         * saleReturnWorkReport,销售退货返修报工
         * OutsourcingPlan 委外计划
         * outsourcingOrder 委外订单
         */
        List<JSONObject> reasonWorkOrder = this.queryNotFinish("workOrder","status","finish","生产工单","orderNumber");
        reasonList.addAll(reasonWorkOrder);
        List<JSONObject> reasonMaterials = this.queryNotExamine("materialRecord","approvalStatus","生产领料派料单","number");
        reasonList.addAll(reasonMaterials);
        List<JSONObject> reasonReturns = this.queryNotExamine("materialReturn","approvalStatus","生产退料单","number");
        reasonList.addAll(reasonReturns);
        List<JSONObject> reasonWorkReport = this.queryNotFinish("workReport","status","finish","生产报工单","orderNumber");
        reasonList.addAll(reasonWorkReport);
        List<JSONObject> reasonRepair = this.queryNotExamine("repairWorkOrder","approvalStatus","返修单","repairNumber");
        reasonList.addAll(reasonRepair);
        List<JSONObject> reasonRepairReport = this.queryNotExamine("repairWorkReport","reportFlag","返修报工单","repairNumber");
        reasonList.addAll(reasonRepairReport);
        List<JSONObject> reasonWorkQuality = this.queryNotExamine("workQuality","approvalStatus","返修质检单","number");
        reasonList.addAll(reasonWorkQuality);
        List<JSONObject> reasonReturnNotice = this.queryNotExamine("returnQualityNotice","approvalStatus","退货质检单","number");
        reasonList.addAll(reasonReturnNotice);
        List<JSONObject> reasonDelivery = this.queryNotExamine("workQualityDelivery","approvalStatus","物流派送单","number");
        reasonList.addAll(reasonDelivery);
        List<JSONObject> PurchasePlan = this.queryNotExamine("PurchasePlan","isApproved","采购计划","code");
        reasonList.addAll(PurchasePlan);
        List<JSONObject> PurchaseOrder = this.queryNotExamine("PurchaseOrder","isApproved","采购订单","code");
        reasonList.addAll(PurchaseOrder);
        List<JSONObject> PurchaseReturn = this.queryNotExamine("PurchaseReturn","isApproved","采购/委外退货单","code");
        reasonList.addAll(PurchaseReturn);
        List<JSONObject> PurchaseReplenish = this.queryNotExamine("PurchaseReplenish","isApproved","采购/委外补货单","code");
        reasonList.addAll(PurchaseReplenish);
        List<JSONObject> receiptOrder = this.queryNotExamine("receiptOrder","isApproved","采购/委外入库单","code");
        reasonList.addAll(receiptOrder);
        List<JSONObject> saleOrder = this.queryNotExamine("saleOrder","isApproved","销售订单","code");
        reasonList.addAll(saleOrder);
        List<JSONObject> saleOutBound = this.queryNotExamine("saleOutBound","isApproved","销售出库单","code");
        reasonList.addAll(saleOutBound);
        List<JSONObject> saleReturn = this.queryNotExamine("PurchaseReturn","isApproved","销售退货入库单","code");
        reasonList.addAll(saleReturn);
        List<JSONObject> saleReturnWorkOrder = this.queryNotExamine("saleReturnWorkOrder","isApproved","销售退货返修单","code");
        reasonList.addAll(saleReturnWorkOrder);
        List<JSONObject> saleReplenish = this.queryNotExamine("saleReplenish","isApproved","销售补货单","code");
        reasonList.addAll(saleReplenish);
        List<JSONObject> returnQualityNotice = this.queryNotExamine("returnQualityNotice","isApproved","销售退货返修质检","code");
        reasonList.addAll(returnQualityNotice);
        List<JSONObject> saleReturnWorkReport = this.queryNotExamine("saleReturnWorkReport","isApproved","销售退货返修报工","code");
        reasonList.addAll(saleReturnWorkReport);
        List<JSONObject> OutsourcingPlan = this.queryNotExamine("OutsourcingPlan","isApproved","委外计划","code");
        reasonList.addAll(OutsourcingPlan);
        List<JSONObject> outsourcingOrder = this.queryNotExamine("outsourcingOrder","isApproved","委外订单","code");
        reasonList.addAll(outsourcingOrder);
        Integer status3 = 1;
        if(reasonWorkOrder.size()>0||reasonMaterials.size()>0||reasonReturns.size()>0
                ||reasonWorkReport.size()>0||reasonRepair.size()>0||reasonRepairReport.size()>0
                ||reasonWorkQuality.size()>0||reasonReturnNotice.size()>0
                ||reasonDelivery.size()>0)
        {
            status3 = 0;
        }
        JSONObject process3 = new JSONObject();
        process3.put("step",3);
        process3.put("status",status3);
        processList.add(process3);
        //是否生已获取成本单价（ERP接口）
        //是否完成当月折旧
        List<JSONObject> reasons5 = this.queryExistOrder("Depreciation",year,month,5,"设备折旧");
        reasonList.addAll(reasons5);
        Integer status5 = reasons5.size()>0?0:1;
        JSONObject process5 = new JSONObject();
        process5.put("step",5);
        process5.put("status",status5);
        processList.add(process5);
        //是否完成当月费用录入
        List<JSONObject> reasons6 = this.queryExistOrder("Expense",year,month,6,"费用录入");
        reasonList.addAll(reasons6);
        Integer status6 = reasons6.size()>0?0:1;
        JSONObject process6 = new JSONObject();
        process6.put("step",6);
        process6.put("status",status6);
        processList.add(process6);
        //是否完成废料录入
        List<JSONObject> reasons7 = this.queryExistOrder("MoldCost",year,month,7,"废料录入");
        reasonList.addAll(reasons7);
        Integer status7 = reasons7.size()>0?0:1;
        JSONObject process7 = new JSONObject();
        process7.put("step",7);
        process7.put("status",status7);
        processList.add(process7);
        //是否已结算工资
        List<JSONObject> reasons8 = this.queryExistOrder("salaryTable",year,month,8,"结算工资");
        reasonList.addAll(reasons8);
        Integer status8 = reasons8.size()>0?0:1;
        JSONObject process8 = new JSONObject();
        process8.put("step",8);
        process8.put("status",status8);
        processList.add(process8);
        //是否已进行成本分摊
        List<JSONObject> reasons9 = this.queryExistOrder("CostAllocation",year,month,9,"成本分摊");
        reasonList.addAll(reasons9);
        Integer status9 = reasons9.size()>0?0:1;
        JSONObject process9 = new JSONObject();
        process9.put("step",9);
        process9.put("status",status9);
        processList.add(process9);
        // 生成结转单（表单）
        Integer status10 = 1;
        if(reasonList.size()==0){
            //查询库存数据
            List<JSONObject> stockDetails = this.queryStockDetail(beginTime,endTime);
            //查询成本分摊数据
            List<JSONObject> costAllocationData = this.initCostAllocationData(year+"-"+month);
            Map<String,BigDecimal> priceMap = new HashMap<>();
            costAllocationData.stream().forEach(item->{
                priceMap.put(item.getJSONObject("values").getString("bcode"),item.getJSONObject("values").getBigDecimal("price"));
            });
            //查询上月月结表数据
            int newMonth = month;
            int newYear = year;
            if(newMonth==1){
                newYear = newYear -1;
                newMonth = 12;
            }else{
                newMonth = newMonth -1;
            }
            List<JSONObject> monthBalanceData = this.initMonthBalanceData(newYear+"-"+newMonth);
            List<JSONObject> periodOrderData = monthBalanceData.stream().map(item->{
                JSONObject json =  new JSONObject();
                json.put("bcode",item.getString("bcode"));
                json.put("periodQuantity",item.getBigDecimal("actualQuantity")==null?new BigDecimal(0):item.getBigDecimal("actualQuantity"));
                json.put("periodAmount",item.getBigDecimal("actualAmount")==null?new BigDecimal(0):item.getBigDecimal("actualAmount"));
                return  json;
            }).collect(Collectors.toList());
            if(monthBalanceData.size()==0){
                //若不存在月结表数据，查询期初单数据
                periodOrderData = this.initPeriodOrderData();
            }
            //stock_type 1入库 0出库,
            Map<Integer,List<JSONObject>> warehouseMap = stockDetails.stream().filter(item->item.getInteger("warehouse_id")!=null).collect(Collectors.groupingBy(item->item.getInteger("warehouse_id")));
            List<JSONObject> addDatas = new ArrayList<>();
            for (Map.Entry<Integer, List<JSONObject>> entry : warehouseMap.entrySet()) {
                Integer warehouseId = entry.getKey();
                List<JSONObject> warehouseDetails = warehouseMap.get(warehouseId);
                //仓库内根据bcode分组
                Map<String,List<JSONObject>> warehouseDetailsMap = warehouseDetails.stream().filter(item->item.getString("material_bcode")!=null).collect(Collectors.groupingBy(item->item.getString("material_bcode")));
                for (Map.Entry<String, List<JSONObject>> entry1 : warehouseDetailsMap.entrySet()) {
                    JSONObject addData = new JSONObject();
                    addData.put("type","logic");
                    addData.put("className","MonthBalance");
                    JSONObject json = new JSONObject();
                    String bcode = entry1.getKey();
                    List<JSONObject> details = warehouseDetailsMap.get(bcode);
                    JSONObject detail = details.get(0);
                    json.put("month",year+"-"+month);
                    json.put("warehouseId",warehouseId);
                    json.put("warehouse",detail.getString("warehouse_code"));
                    json.put("pcode",detail.getString("material_pcode"));
                    json.put("bcode",bcode);
                    //期初数量和金额
                    BigDecimal periodQuantity = new BigDecimal(0);
                    BigDecimal periodAmount = new BigDecimal(0);
                    List<JSONObject> filterPeriod = periodOrderData.stream().filter(item->bcode.equals(item.getString("bcode"))).collect(Collectors.toList());
                    if(filterPeriod.size()>0){
                        periodQuantity = filterPeriod.get(0).getBigDecimal("periodQuantity");
                        periodAmount = filterPeriod.get(0).getBigDecimal("periodAmount");
                    }
                    //入库排除盘盈
                    BigDecimal inCollectQuantity = details.stream()
                            .filter(item -> item.getInteger("stock_type") == 1&&(!"14".equals(item.getString("form_type"))))
                            .map(dto -> dto.getBigDecimal("quantity"))
                            .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                    //出库排除盘亏
                    BigDecimal outCollectQuantity = details.stream().filter(item -> item.getInteger("stock_type") == 2&&(!"15".equals(item.getString("form_type"))))
                            .map(dto -> dto.getBigDecimal("quantity"))
                            .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                    //入库
                    BigDecimal inAllQuantity = details.stream()
                            .filter(item -> item.getInteger("stock_type") == 1)
                            .map(dto -> dto.getBigDecimal("quantity"))
                            .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                    //出库
                    BigDecimal outAllQuantity = details.stream().filter(item -> item.getInteger("stock_type") == 2)
                            .map(dto -> dto.getBigDecimal("quantity"))
                            .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                    //理论结存数量
                    BigDecimal theoreticalQuantity = periodQuantity.add(inCollectQuantity).subtract(outCollectQuantity);
                    //实际结存数量
                    BigDecimal actualQuantity = periodQuantity.add(inAllQuantity).subtract(outAllQuantity);
                    BigDecimal price = priceMap.get(bcode)==null?new BigDecimal(0):priceMap.get(bcode);
                    //期初数量
                    json.put("startQuantity",periodQuantity);
                    //期初金额
                    json.put("startAmount",periodAmount);
                    //入库汇总数量
                    json.put("inCollectQuantity",inCollectQuantity);
                    //出库汇总数量
                    json.put("outCollectQuantity",outCollectQuantity);
                    //理论结存数量
                    json.put("theoreticalQuantity",theoreticalQuantity);
                    //实际结存数量
                    json.put("actualQuantity",actualQuantity);
                    //入库汇总金额
                    json.put("inCollectAmount",inCollectQuantity.multiply(price));
                    //出库汇总金额
                    json.put("outCollectAmount",outCollectQuantity.multiply(price));
                    //理论结存金额
                    json.put("theoreticalAmount",theoreticalQuantity.multiply(price));
                    //实际结存金额
                    json.put("actualAmount",actualQuantity.multiply(price));
                    addData.put("values",json);
                    addDatas.add(addData);
                }
            }
            //删除传入年月的结转单
            this.logicInstanceDslService.deleteByCondition("MonthBalance","type='logic' and className='MonthBalance' and month = '"+year+"-"+month+"'");
            this.logicInstanceDslService.batchAdd(addDatas);
        }else{
            status10 = 0;
            JSONObject reason = new JSONObject();
            reason.put("step",10);
            reason.put("docType","生成结转单");
            reason.put("docStatus","会计期未启用");
            reason.put("count",1);
            reasonList.add(reason);
        }
        JSONObject process10 = new JSONObject();
        process10.put("step",10);
        process10.put("status",status10);
        processList.add(process10);
        //将会计期设置为下一个月
        Integer status11 = 1;
        if(reasonList.size()==0){
            //将会计期启用状态更新为下一个月
            String updCon = "type='logic' and className='AccountPeriod' and status=true";
            JSONObject updData = new JSONObject();
            JSONObject values = new JSONObject();
            values.put("status",false);
            updData.put("values",values);
            this.logicInstanceDslService.updateByCondition(AccountPeriodClassName,updCon,updData);
            JSONObject updateData = new JSONObject();
            values.put("status",true);
            updateData.put("values",values);
            if(month==12){
                year = year +1;
                month = 1;
            }else{
                month = month +1;
            }
            this.logicInstanceDslService.updateByCondition("AccountPeriod","",updateData);
        }else {
            status11 = 0;
            JSONObject reason = new JSONObject();
            reason.put("step",11);
            reason.put("docType","设置会计期为下一个月");
            reason.put("docStatus","存在失败单据");
            reason.put("count",1);
            reasonList.add(reason);
        }
        JSONObject process11 = new JSONObject();
        process11.put("step",11);
        process11.put("status",status11);
        processList.add(process11);
        //完成月末结转
        Integer status12 = 1;
        if(reasonList.size()>0) {
            status12 = 0;
            JSONObject reason = new JSONObject();
            reason.put("step",12);
            reason.put("docType","完成月末结转");
            reason.put("docStatus","存在失败单据");
            reason.put("count",1);
            reasonList.add(reason);
        }
        JSONObject process12 = new JSONObject();
        process12.put("step",12);
        process12.put("status",status12);
        processList.add(process12);
        //删除传入年月的结转单
        this.logicInstanceDslService.deleteByCondition("CarryForward","type='logic' and className='CarryForward' and fiscalYear="+year+" and fiscalMonth="+month);
        //新增结转数据
        JSONObject addData = new JSONObject();
        JSONObject values = new JSONObject();
        addData.put("type","logic");
        addData.put("className","CarryForward");
        values.put("fiscalYear",year);
        values.put("fiscalMonth",month);
        values.put("beginTime",beginTime);
        values.put("endTime",endTime);
        addData.put("values",values);
        JSONObject addIns = this.logicInstanceDslService.add(addData);
        JSONObject addHierReason = new JSONObject();
        List<JSONObject> hierArrReason = reasonList.stream().map(reason->{
            JSONObject hier = new JSONObject();
            hier.put("hierValues",reason);
            return  hier;
        }).collect(Collectors.toList());
        addHierReason.put("model","Logic");
        addHierReason.put("containerId",addIns.getInteger("id"));
        addHierReason.put("parentNodeId","3yes0cjbh6a000");
        addHierReason.put("dataArray",hierArrReason);
        List<HierInstance> addHierInsReason =  this.hierInstanceDslService.addNodeAndLinkInstanceToListContainer(addHierReason);
        JSONObject addHierProcess = new JSONObject();
        List<JSONObject> hierArrProcess = processList.stream().map(process->{
            JSONObject hier = new JSONObject();
            hier.put("hierValues",process);
            return  hier;
        }).collect(Collectors.toList());
        addHierProcess.put("model","Logic");
        addHierProcess.put("containerId",addIns.getInteger("id"));
        addHierProcess.put("parentNodeId","1tpltnxy89r400");
        addHierProcess.put("dataArray",hierArrProcess);
        List<HierInstance> addHierInsProcess =  this.hierInstanceDslService.addNodeAndLinkInstanceToListContainer(addHierProcess);
        List<JSONObject> ins = this.logicInstanceDslService.query("type = 'logic' and className = 'CarryForward' and id = "+addIns.getInteger("id").toString(),"CarryForward");
        return JsonResult.success(ins);
    }
    /**
     * 反月末结转接口
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultInfo<?> carryForwardReturn(){
        String con1 = "type='logic' and className='AccountPeriod' and status=true";
        List<JSONObject> yearIns = this.logicInstanceDslService.query(con1,AccountPeriodClassName);
        if(yearIns.size()==0){
            return JsonResult.error(506,"不存在启用的会计期，请检查！");
        }
        Integer year = yearIns.get(0).getJSONObject("values").getInteger("fiscalYear");
        Integer month = yearIns.get(0).getJSONObject("values").getInteger("fiscalMonth");
        String yearMonth = year+"-"+month;
        Integer newYear = year;
        Integer newMonth = month;
        String newYearMonth = newYear+"-"+newMonth;
        //删除上年月的月结表和月末结转表，更新上年月未启用状态
        this.logicInstanceDslService.deleteByCondition("CarryForward","type='logic' and className='CarryForward' and fiscalYear="+newYear+" and fiscalMonth="+newMonth);
//删除传入年月的结转单
        this.logicInstanceDslService.deleteByCondition("MonthBalance","type='logic' and className='MonthBalance' and month = '"+newYearMonth+"'");
        String updCon1 = "type='logic' and className='AccountPeriod' and status=true";
        JSONObject updData1 = new JSONObject();
        JSONObject values1 = new JSONObject();
        values1.put("status",false);
        updData1.put("values",values1);
        this.logicInstanceDslService.updateByCondition(AccountPeriodClassName,updCon1,updData1);
        String updCon2 = "type='logic' and className='AccountPeriod' and status=true";
        JSONObject updData2 = new JSONObject();
        JSONObject values2 = new JSONObject();
        values2.put("fiscalYear",newYear);
        values2.put("fiscalMonth",newMonth);
        updData2.put("values",values2);
        this.logicInstanceDslService.updateByCondition(AccountPeriodClassName,updCon2,updData2);
        return JsonResult.success("返结转成功");
    }
    /**
     * 价格调整接口
     * 参数：实例Id id 业务类型 businessType 开始时间  beginTime  结束时间 endTime
     * businessType：采购 1 ，委外  2 ，销售  3
     * @return
     */
    public ResultInfo<?> computeWasteQuantity(JSONObject dataObj){
        /**
         * 根据ID查询出价格调整的hier数据
         * 根据开始和结束时间，拿到对应类型的实例，再去更新相关字段
         */
        Long id = dataObj.getLong("id");
        List<HierInstance> hiers = this.hierInstanceDslService.queryByContainerId("logic",id,"Waste","parentId = 'lmpvx8v3q1c00'");

        return JsonResult.success("");
    }

    /**
     * 价格调整接口
     * 参数：实例Id id 业务类型 businessType 开始时间  beginTime  结束时间 endTime
     * businessType：采购 1 ，委外  2 ，销售  3
     * @return
     */
    public ResultInfo<?> priceAdjustment(JSONObject dataObj){
        /**
         * 根据ID查询出价格调整的hier数据
         * 根据开始和结束时间，拿到对应类型的实例，再去更新相关字段
         */
        Long id = dataObj.getLong("id");
        Date beginTime = dataObj.getDate("beginTime");
        Date endTime = dataObj.getDate("endTime");
        Integer businessType = dataObj.getInteger("businessType");
        List<HierInstance> hiers = this.hierInstanceDslService.queryByContainerId("logic",id,"priceAdjustment","parentId = '1m71x1llj7mo00'");
        switch (businessType){
            case 1:
                priceAdjustmentForPurchase(hiers,beginTime,endTime);
                break;
            case 2:
                priceAdjustmentForOutsourcing(hiers,beginTime,endTime);
                break;
            case 3:
                priceAdjustmentForSale(hiers,beginTime,endTime);
                break;
            default:
                break;
        }

        return JsonResult.success("调整完毕");
    }

    /**
     * 调整hier子表价格
     */
    private void priceAdjustmentForPurchase(List<HierInstance> hiers,Date beginTime,Date endTime){
        String startDate = DateUtils.getDateFormat(beginTime, "yyyy-MM-dd 00:00:00");
        String endDate = DateUtils.getDateFormat(endTime, "yyyy-MM-dd 23:59:59");
        String condition = " date >= '" + startDate + "' and date <= '" + endDate + "'";
        List<Long> ids = this.logicInstanceDslService.queryIdWithoutHier(condition, "PurchaseOrder");
        //订单产品列表
        this.priceAdjustmentHiers(hiers,ids,"PurchaseOrder","parentId = 'a7bgmkl8f8000'",true,"supplier_code","amount","amountTax");
        //赋码明细
        this.priceAdjustmentHiers(hiers,ids,"PurchaseOrder","parentId = '3lcfo6nswbg000'",false,"supplier_code",null,null);

    }

    /**
     * 调整采购物料明细价格、非税金额、含税金额、非税单价、含税单价、税率
     * @param adjustHiers 调整价格列表
     * @param ids       订单ids
     */
    private void priceAdjustmentHiers(List<HierInstance> adjustHiers,List<Long> ids,String className,String condition,boolean hasMoney,String supplierCodeKey,String moneyKey,String moneyNonTaxKey){
        ids.forEach(id->{
            JSONObject jsonObject = this.logicInstanceDslService.queryJsonById(id);
            JSONObject jsonValues = jsonObject.getJSONObject("values");
            String supplier = jsonValues.getString(supplierCodeKey);
            supplier = StringUtils.isNotBlank(supplier) ? supplier : "supplierNull";
            List<HierInstance> hiers = this.hierInstanceDslService.queryByContainerId("logic",id,className,condition);
            String finalSupplier = supplier;
            hiers.forEach(hIns->{
                JSONObject values = hIns.getValues();
                Double quantity = values.getDouble("quantity");
                String materialBCode = values.getString("material_bcode");
                HierInstance hierInstance = adjustHiers.stream().filter(ins -> ins.getValues().getString("bcode").equalsIgnoreCase(materialBCode) && ins.getValues().getString("supplier").equalsIgnoreCase(finalSupplier)).findFirst().orElse(null);
                if(hierInstance!=null){
                    JSONObject adjustValues = hierInstance.getValues();
                    Double price = adjustValues.getDouble("hasTaxUnitPrice");
                    Double priceNonTax = adjustValues.getDouble("noTaxUnitPrice");
                    Double taxRate = adjustValues.getDouble("taxRate");
                    if(price!=null){
                        priceNonTax = price * (1-taxRate);
                    }else if(priceNonTax != null){
                        price = priceNonTax * (1+taxRate);
                    }
                    values.put("price", price);
                    values.put("priceNonTax", priceNonTax);
                    values.put("taxRate", taxRate);
                    if(hasMoney){
                        values.put(moneyKey, TypeUtils.roundNumber(quantity * price, 4));
                        values.put(moneyNonTaxKey, TypeUtils.roundNumber(quantity * priceNonTax, 4));
                    }
                }
                hIns.setValues(values);
            });
            this.hierInstanceDslService.updateBatchById(hiers);
        });
    }

    /**
     * 调整实例价格、非税金额、含税金额、非税单价、含税单价、税率
     * @param adjustHiers 调整价格列表
     * @param ids       订单ids
     */
    private void priceAdjustmentInstance(List<HierInstance> adjustHiers,List<Long> ids, boolean hasMoney,String supplierCodeKey,String moneyKey,String moneyNonTaxKey){
        ids.forEach(id->{
            JSONObject jsonObject = this.logicInstanceDslService.queryJsonById(id);
            JSONObject values = jsonObject.getJSONObject("values");
            Double quantity = values.getDouble("quantity");
            String materialBCode = values.getString("material_bcode");
            String supplier = values.getString(supplierCodeKey);
            supplier = StringUtils.isNotBlank(supplier) ? supplier : "supplierNull";
            String finalSupplier = supplier;
            HierInstance hierInstance = adjustHiers.stream().filter(ins -> ins.getValues().getString("material_bcode").equalsIgnoreCase(materialBCode) && ins.getValues().getString("supplier").equalsIgnoreCase(finalSupplier)).findFirst().orElse(null);
            if (hierInstance != null) {
                JSONObject adjustValues = hierInstance.getValues();
                Double price = adjustValues.getDouble("price");
                Double priceNonTax = adjustValues.getDouble("priceNonTax");
                Double taxRate = adjustValues.getDouble("taxRate");
                if (price != null) {
                    priceNonTax = price * (1 - taxRate);
                } else if (priceNonTax != null) {
                    price = priceNonTax * (1 + taxRate);
                }
                values.put("price", price);
                values.put("priceNonTax", priceNonTax);
                values.put("taxRate", taxRate);
                if (hasMoney) {
                    values.put(moneyKey, TypeUtils.roundNumber(quantity * price, 2));
                    values.put(moneyNonTaxKey, TypeUtils.roundNumber(quantity * priceNonTax, 2));
                }
            }
            jsonObject.put("values", values);
            this.logicInstanceDslService.updateById(jsonObject);
        });
    }


    /**
     *
     * @param ids    订单ids
     * @param className  单据类名
     * @param condition  查询条件
     * @param moneyKey    含税总额
     * @param moneyNonTaxKey 非税总额
     */
    private void priceAdjustmentInstanceSum(List<Long> ids, String className, String condition, String moneyKey, String moneyNonTaxKey){
        ids.forEach(id->{
            List<HierInstance> hiers = this.hierInstanceDslService.queryByContainerId("logic",id,className,condition);
            double money = hiers.stream().mapToDouble(d -> d.getValues() == null ? 0.0 : d.getValues().getDouble(moneyKey)).sum();
            double moneyNonTax = hiers.stream().mapToDouble(d -> d.getValues() == null ? 0.0 : d.getValues().getDouble(moneyNonTaxKey)).sum();
            JSONObject jsonObject = this.logicInstanceDslService.queryJsonById(id);
            JSONObject values = jsonObject.getJSONObject("values");
            values.put("money",money);
            values.put("moneyNonTax",moneyNonTax);
            jsonObject.put("values",values);
            this.logicInstanceDslService.updateById(jsonObject);
        });
    }


    /**
     * 调整委外价格
     */
    private void priceAdjustmentForOutsourcing(List<HierInstance> hiers, Date beginTime, Date endTime) {


    }

    /**
     * 调整销售价格
     */
    private void priceAdjustmentForSale(List<HierInstance> hiers, Date beginTime, Date endTime) {
        String startDate = DateUtils.getDateFormat(beginTime, "yyyy-MM-dd 00:00:00");
        String endDate = DateUtils.getDateFormat(endTime, "yyyy-MM-dd 23:59:59");
        String condition = " date >= '" + startDate + "' and date <= '" + endDate + "'";
        List<Long> ids = this.logicInstanceDslService.queryIdWithoutHier(condition, "saleOrder");
        //1、销售订单、试制订单产品列表
        this.priceAdjustmentHiers(hiers,ids,"saleOrder","parentId = 'root'",true,"customer_code","money","moneyNonTax");
        //1、销售订单、试制订单汇总主表数据
        this.priceAdjustmentInstanceSum(ids,"saleOrder","parentId = 'root'","money","moneyNonTax");
        //2、销售补货单Ids
        List<Long> replenishIds = this.logicInstanceDslService.queryIdWithoutHier(condition, "saleReplenish");
        //2、销售补货单物料明细价格更新
        this.priceAdjustmentHiers(hiers,replenishIds,"saleReplenish","parentId = '4q6hdvjjym4000'",true,"customer_code","money","moneyNonTax");
        //3、销售退货入库单Ids
        List<Long> returnIds = this.logicInstanceDslService.queryIdWithoutHier(condition, "saleReturn");
        //3、销售退货入库单产品明细价格更新
        this.priceAdjustmentHiers(hiers,returnIds,"saleReturn","parentId = '3h7yargwkak000'",true,"customer_code","money","moneyNonTax");
        //4、销售退货入库单Ids
        List<Long> outBoundIds = this.logicInstanceDslService.queryIdWithoutHier(condition, "saleOutBound");
        //4、销售退货入库单产品明细价格更新
        this.priceAdjustmentHiers(hiers,outBoundIds,"saleOutBound","parentId = '5c7xxyrwwh0000'",true,"customer_code","money","moneyNonTax");
    }

    /**
     * 查询是否存在相应数据
     * @param className
     * @param year
     * @param month
     * @param step
     * @param docType
     * @return
     */
    private List<JSONObject> queryExistOrder(String className,Integer year,Integer month,Integer step,String docType){
        String con5 = "type = 'logic' and className = '%s' and yearMonth = '"+year+"-"+month+"'";
        List<JSONObject> ins5 = this.logicInstanceDslService.query(String.format(con5,className),className);
        List<JSONObject> reasons = new ArrayList<>();
        if(ins5.size()==0){
            JSONObject reason = new JSONObject();
            reason.put("step",step);
            reason.put("docType",docType);
            reason.put("docStatus","此年月不存在"+docType+"数据");
            reason.put("count",1);
            reasons.add(reason);
        }
        return reasons;
    }

    /**
     * 查询库存明细数据
     * @param beginTime
     * @param endTime
     * @return
     */
    public List<JSONObject> queryStockDetail(String beginTime,String endTime){
        //未作废且在时间段内
        beginTime = "2023-11-01 00:00:00";
        endTime = "2023-11-30 23:59:59";
        String con = "type = 'logic' and className = 'stockDetail' and status != 9 and createTime >='"+beginTime+"' and createTime <= '"+endTime+"'";
        List<JSONObject> stockDetailIns = this.logicInstanceDslService.query(con,"stockDetail");
        List<JSONObject> stockDetails = stockDetailIns.stream().map(ins->ins.getJSONObject("values")).collect(Collectors.toList());
        return stockDetails;
    }
    /**
     * 查询是否存在未完成数据
     * @param className
     * @param statusKey
     * @param statusValue
     * @param docType
     * @param codeKey
     * @return
     */
    private List<JSONObject> queryNotFinish(String className,String statusKey,String statusValue,String docType,String codeKey){
        String queryExamine = "type = 'logic' and className = '%s' and "+statusKey+" != '"+statusValue+"'";
        List<JSONObject> materialRecordIns = this.logicInstanceDslService.query(String.format(queryExamine,className),className);
        List<JSONObject> reasons = new ArrayList<>();
        if(materialRecordIns.size()>0){
            reasons = materialRecordIns.stream().map(ins->{
                JSONObject reason = new JSONObject();
                reason.put("step",3);
                reason.put("docType",docType);
                reason.put("docStatus",ins.getJSONObject("values").getString(codeKey)+"未完成！");
                reason.put("count",1);
                return reason;
            }).collect(Collectors.toList());
        }
        return reasons;
    }
    /**
     * 查询是否存在未审批数据
     * @param className
     * @param statusKey
     * @param docType
     * @param codeKey
     * @return
     */
    private List<JSONObject> queryNotExamine(String className,String statusKey,String docType,String codeKey){
        String queryExamine = "type = 'logic' and className = '%s' and ("+statusKey+" != true or "+statusKey+" != 1)";
        List<JSONObject> materialRecordIns = this.logicInstanceDslService.query(String.format(queryExamine,className),className);
        List<JSONObject> reasons = new ArrayList<>();
        if(materialRecordIns.size()>0){
            reasons = materialRecordIns.stream().map(ins->{
                JSONObject reason = new JSONObject();
                reason.put("step",3);
                reason.put("docType",docType);
                reason.put("docStatus",ins.getJSONObject("values").getString(codeKey)+"未审核！");
                reason.put("count",1);
                return reason;
            }).collect(Collectors.toList());
        }
        return reasons;
    }

    /**
     * 计算模具工时单价
     * @param mouldList
     * @param mouldCostMap
     * @return
     */
    private List<JSONObject> computeMouldPrice(List<JSONObject> mouldList,Map<String,JSONObject> mouldCostMap){
        List<JSONObject> mouldPriceList = new ArrayList<>();
        Map<String,List<JSONObject>> mouldMapForMould = mouldList.stream().collect(Collectors.groupingBy(dto -> dto.getString("mould")));
        for (Map.Entry<String, List<JSONObject>> entry : mouldMapForMould.entrySet()) {
            String mould = entry.getKey();
            List<JSONObject> list = mouldMapForMould.get(mould);
            BigDecimal hourAll = list.stream().map(dto -> dto.getBigDecimal("hour"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal hourPrice = new BigDecimal(0);//如果模具本月没有模具费
            if(mouldCostMap.get(mould)!=null){//有折旧
                hourPrice = mouldCostMap.get(mould).getBigDecimal("amount").divide(hourAll,2,BigDecimal.ROUND_HALF_UP);
            }
            JSONObject json = new JSONObject();
            json.put("mould",mould);
            json.put("hourAll",hourAll);
            json.put("hourPrice",hourPrice);
            mouldPriceList.add(json);
        }
        return mouldPriceList;
    }
    /**
     * 计算设备的工时单价
     * @param equipmentList
     * @param equMap
     * @return
     */
    private List<JSONObject> computeEquipmentPrice(List<JSONObject> equipmentList,Map<Integer,JSONObject> equMap){
        List<JSONObject> equPriceList = new ArrayList<>();
        Map<Integer,List<JSONObject>> equMapForEqu = equipmentList.stream().collect(Collectors.groupingBy(dto -> dto.getInteger("equipment")));
        for (Map.Entry<Integer, List<JSONObject>> entry : equMapForEqu.entrySet()) {
            Integer equipment = entry.getKey();
            List<JSONObject> list = equMapForEqu.get(equipment);
            BigDecimal hourAll = list.stream().map(dto -> dto.getBigDecimal("hour"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal hourPrice = new BigDecimal(0);//如果设备本月没有折旧
            if(equMap.get(equipment)!=null){//有折旧
                hourPrice = equMap.get(equipment).getBigDecimal("depreciationMonth").divide(hourAll,2,BigDecimal.ROUND_HALF_UP);
            }
            JSONObject json = new JSONObject();
            json.put("equipment",equipment);
            json.put("hourAll",hourAll);
            json.put("hourPrice",hourPrice);
            equPriceList.add(json);
        }
        return equPriceList;
    }
    /**
     * 计算设备单价
     * @param laborList
     * @param salaryMap
     * @return
     */
    private List<JSONObject> computeLaborPrice(List<JSONObject> laborList,Map<Integer,JSONObject> salaryMap){
        List<JSONObject> laborPriceList = new ArrayList<>();
        Map<Integer,List<JSONObject>> laborMapForPerson = laborList.stream().collect(Collectors.groupingBy(dto -> dto.getInteger("operator")));
        for (Map.Entry<Integer, List<JSONObject>> entry : laborMapForPerson.entrySet()) {
            Integer person = entry.getKey();
            //计件类型
            String allocationWay = salaryMap.get(person).getString("allocationWay");
            String attribute = salaryMap.get(person).getString("attribute");
            List<JSONObject> list = laborMapForPerson.get(person);
            BigDecimal hourAll = list.stream().map(dto -> dto.getBigDecimal("hour"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal hourPrice = salaryMap.get(person).getBigDecimal("netSalary").divide(hourAll,2,BigDecimal.ROUND_HALF_UP);
            BigDecimal quantityAll = list.stream().map(dto -> dto.getBigDecimal("quantity"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal quantityPrice = salaryMap.get(person).getBigDecimal("netSalary").divide(quantityAll,2,BigDecimal.ROUND_HALF_UP);
            JSONObject json = new JSONObject();
            json.put("person",person);
            json.put("hourAll",hourAll);
            json.put("quantityAll",quantityAll);
            json.put("hourPrice",hourPrice);
            json.put("quantityPrice",quantityPrice);
            json.put("allocationWay",allocationWay);
            json.put("attribute",attribute);
            laborPriceList.add(json);
        }
        return laborPriceList;
    }

    /**
     * 初始化报工数据
     * @param yearMonth
     * @param reportList
     * @param laborList
     * @param equipmentList
     * @param mouldList
     * @param laborMap
     * @param equipmentMap
     * @param mouldMap
     * @param deductList
     */
    private void initReportList(String yearMonth,List<JSONObject> reportList,
                                List<JSONObject> laborList,List<JSONObject> equipmentList,List<JSONObject> mouldList,
                                Map<String,List<JSONObject>> laborMap,Map<String,List<JSONObject>> equipmentMap,Map<String,List<JSONObject>> mouldMap,
                                List<JSONObject> deductList)
    {
        String[] yearMonths = yearMonth.split("-");
        int year = Integer.valueOf(yearMonths[0]);
        int month = Integer.valueOf(yearMonths[1]);
        //组合BOM对象列表
        List<ProductBomMaster> combinedBomList = this.initCombineBomList();
        //拆分BOM对象列表
        List<ProductBomMaster> splitBomList = this.initSplitBomList();
        //key是作为产品的bcode的key
        Map<String,ProductBomMaster> combinedBomMap = combinedBomList.stream().collect(Collectors.toMap(dto -> dto.getMaterialBCode(), dto->dto,
                (key1, key2) -> key2));
        Map<String,ProductBomMaster> splitBomMap = new HashMap<String,ProductBomMaster>();
        splitBomList.stream().forEach(dto-> {
            dto.getProductBomSubList().forEach(dto1->{
                splitBomMap.put(dto1.getMaterialBCode(),dto);
            });
        });
        //查询会计期开始和结束时间
        String con1 = "type='logic' and className='AccountPeriod' and fiscalYear="+year+" and fiscalMonth="+month;
        List<JSONObject> yearIns = this.logicInstanceDslService.query(con1,AccountPeriodClassName);
        if(yearIns.size()==0){
            throw new BusinessException(500, yearMonth+"会计期不存在！");
        }
        String beginTime =yearIns.get(0).getJSONObject("values").getString("beginTime");
        String endTime =yearIns.get(0).getJSONObject("values").getString("endTime");
        beginTime = "2023-08-01 00:00:00";
        endTime = "2023-11-30 23:59:59";
        //查询报工明细结束时间在开始和结束时间之内的
//        String con2 = "type='logic' and className='workReport' and parentId='4ja1eoa9f6o000' and workReportEndTime>='"+beginTime+"' and workReportEndTime<='"+endTime+"'";
        String con2 = "parentId='4ja1eoa9f6o000' and workReportEndTime>='"+beginTime+"' and workReportEndTime<='"+endTime+"' and material_bcode != 'b-001'";
        List<HierInstance> reports = this.hierInstanceDslService.query("logic","workReport",con2);
        Map<String, List<HierInstance>> mapByBcode = reports.stream().filter(dto -> dto.getValues().getString("material_bcode")!=null)
                .collect(Collectors.groupingBy(dto -> dto.getValues().getString("material_bcode")));


        for (Map.Entry<String, List<HierInstance>> entry : mapByBcode.entrySet()) {
            JSONObject reportJson = new JSONObject();
            String bcode = entry.getKey();
            reportJson.put("bcode",bcode);
            List<HierInstance> list = mapByBcode.get(bcode);
            //计算背番产量（入，数量）
            BigDecimal workReportNumber = list.stream().filter(dto -> dto.getValues().getBigDecimal("workReportNumber")!=null)
                    .map(dto -> dto.getValues().getBigDecimal("workReportNumber"))
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal poorNumber = list.stream().filter(dto -> dto.getValues().getBigDecimal("workReportNumber")!=null)
                    .map(dto -> dto.getValues().getBigDecimal("poorNumber"))
                            .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal scrapNumber = list.stream().filter(dto -> dto.getValues().getBigDecimal("workReportNumber")!=null)
                    .map(dto -> dto.getValues().getBigDecimal("scrapNumber"))
                            .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal reportNumber = workReportNumber.add(poorNumber).add(scrapNumber);
            reportJson.put("reportNumber",reportNumber);
            //根据背番拿到原料倒扣量（出数量），如果是组合bom就是按照bom比例来计算倒扣库存，如果是拆分BOM则根据比例拿到倒扣库存后除以拆分出的产品数
            List<JSONObject> deductListForPro = new ArrayList<>();
            Boolean splitFlag = false;
            BigDecimal splitRatio = new BigDecimal(0);
            if(combinedBomMap.get(bcode)!=null) {//对应组合bom
                //倒扣数量计算，
                ProductBomMaster finalBom = combinedBomMap.get(bcode);
                reportJson.put("pcode",finalBom.getMaterialPCode());
                List<ProductBomSub> materialList = finalBom.getProductBomSubList();
                materialList.stream().forEach(material->{
                    JSONObject deduct = new JSONObject();
                    BigDecimal deductQuality = reportNumber.divide(new BigDecimal(finalBom.getQuantity()),2,BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(material.getQuantity()));
                    deduct.put("bcode",material.getMaterialBCode());
                    deduct.put("quality",deductQuality);
                    deductListForPro.add(deduct);
                    deductList.add(deduct);
                });
            }else if(splitBomMap.get(bcode)!=null) {//对应拆分bom
                JSONObject deduct = new JSONObject();
                splitFlag = true;
                ProductBomMaster finalBom = splitBomMap.get(bcode);
                reportJson.put("pcode",finalBom.getMaterialPCode());
                List<ProductBomSub> prodList = finalBom.getProductBomSubList();
                BigDecimal materialQuality = BigDecimal.valueOf(finalBom.getQuantity());
                BigDecimal prodQuality = prodList.stream()
                        .filter(prod->bcode.equals(prod.getMaterialBCode()))
                        .map(prod->BigDecimal.valueOf(prod.getQuantity())).reduce(BigDecimal.ZERO,BigDecimal::add);
                BigDecimal totalQuality = prodList.stream().map(prod->BigDecimal.valueOf(prod.getQuantity())).reduce(BigDecimal.ZERO,BigDecimal::add);
                BigDecimal deductQuality = reportNumber.multiply(materialQuality).multiply(prodQuality).divide(totalQuality,2,BigDecimal.ROUND_HALF_UP);
                deduct.put("bcode",finalBom.getMaterialBCode());
                deduct.put("quality",deductQuality);
                splitRatio = prodQuality.divide(totalQuality,2,BigDecimal.ROUND_HALF_UP);
                deductListForPro.add(deduct);
                deductList.add(deduct);
            }else {
                throw new BusinessException(500, "背番号："+bcode+"的产品对应bom不存在");
            }
            reportJson.put("deduct",deductListForPro);

            //计算背番工时，每人的
            Boolean finalSplitFlag = splitFlag;
            BigDecimal finalSplitRatio = splitRatio;
            //人工工时及产量
            List<JSONObject> laborListForBcode = new ArrayList<>();
            List<JSONObject> equipmentListForBcode = new ArrayList<>();
            List<JSONObject> mouldListForBcode = new ArrayList<>();
            list.stream().forEach(dto->{
                JSONArray operator = dto.getValues().get("operator")==null?new JSONArray():dto.getValues().getJSONArray("operator");
                BigDecimal diffHour = this.getDifferHour(dto.getValues().getString("workReportBeginTime"),dto.getValues().getString("workReportEndTime"));
                if(finalSplitFlag){
                    diffHour = diffHour.multiply(finalSplitRatio);
                }
                BigDecimal workReportNumber1 = dto.getValues().get("workReportNumber")==null?new BigDecimal(0):dto.getValues().getBigDecimal("workReportNumber");
                BigDecimal poorNumber1 = dto.getValues().get("poorNumber")==null?new BigDecimal(0):dto.getValues().getBigDecimal("poorNumber");
                BigDecimal scrapNumber1 = dto.getValues().get("scrapNumber")==null?new BigDecimal(0):dto.getValues().getBigDecimal("scrapNumber");
                BigDecimal yieldNumber = workReportNumber1.add(poorNumber1).add(scrapNumber1);
                BigDecimal finalDiffHour = diffHour;
                for(int i=0;i<operator.size();i++){
                    Object oper = operator.get(i);
                    JSONObject labor = new JSONObject();
                    labor.put("bcode",bcode);
                    labor.put("operator",oper);
                    labor.put("hour", finalDiffHour);
                    labor.put("quantity",yieldNumber);
                    laborList.add(labor);
                    laborListForBcode.add(labor);
                }
//                JSONArray moulds = dto.getValues().getJSONArray("mould");
                //模具费只存储了模具code
                JSONArray moulds = dto.getValues().get("mould_code")==null?new JSONArray():dto.getValues().getJSONArray("mould_code");
                for(int i=0;i<moulds.size();i++) {
                    Object mould = moulds.get(i);
                    JSONObject mouldJson = new JSONObject();
                    mouldJson.put("bcode",bcode);
                    mouldJson.put("mould",mould);
                    mouldJson.put("hour", finalDiffHour);
                    mouldList.add(mouldJson);
                    mouldListForBcode.add(mouldJson);
                }
                if(dto.getValues().get("equipment")!=null){
                    JSONObject equJson = new JSONObject();
                    equJson.put("bcode",bcode);
                    equJson.put("equipment",dto.getValues().getInteger("equipment"));
                    equJson.put("hour", finalDiffHour);
                    equipmentList.add(equJson);
                    equipmentListForBcode.add(equJson);
                }
            });
            laborMap.put(bcode,laborListForBcode);
            equipmentMap.put(bcode,equipmentListForBcode);
            mouldMap.put(bcode,mouldListForBcode);
            reportList.add(reportJson);
        }

    }
    /**
     * 研发比例
     * @param beginTime
     * @param endTime
     * @return
     */
    public List<JSONObject> initRatio(String beginTime,String endTime){
        //未作废且在时间段内
        beginTime = "2023-09-01 00:00:00";
        endTime = "2023-11-30 23:59:59";
        String con = "type = 'logic' and className = 'saleOrder' and isApproved = true and createTime >='"+beginTime+"' and createTime <= '"+endTime+"'";
        List<JSONObject> saleOrderIns = this.logicInstanceDslService.query(con,"saleOrder");
        //根据试制和非试制分组
        Map<Boolean,List<JSONObject>> saleOrdersByTrial = saleOrderIns.stream().collect(Collectors.groupingBy(dto -> dto.getJSONObject("values").getBoolean("isTrial")));
        //获取试制订单
        List<JSONObject> saleOrdersTrial = saleOrdersByTrial.get(true)==null?new ArrayList<>():saleOrdersByTrial.get(true);
        //获取正常订单
        List<JSONObject> saleOrders = saleOrdersByTrial.get(false)==null?new ArrayList<>():saleOrdersByTrial.get(false);
        //拿到生产bcode的数量
        List<JSONObject> codeList = new ArrayList<>();
        for(int i=0;i<saleOrdersTrial.size();i++){
            JSONObject ins = saleOrdersTrial.get(i);
            JSONArray contents = ins.getJSONObject("hier").getJSONArray("content");
            List<JSONObject> list = new ArrayList<>();
            contents.stream().forEach(hier->{
                JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
                if("root".equals(hierJson.getString("parentId"))){
                    JSONObject json = new JSONObject();
                    json.put("bcode",hierJson.getJSONObject("values").getString("material_bcode"));
                    BigDecimal quantity = hierJson.getJSONObject("values").getBigDecimal("quantity")==null?new BigDecimal(0):hierJson.getJSONObject("values").getBigDecimal("quantity");
                    json.put("isTrial",true);
                    json.put("quantity",quantity);
                    codeList.add(json);
                }
            });
        }
        for(int i=0;i<saleOrders.size();i++){
            JSONObject ins = saleOrders.get(i);
            JSONArray contents = ins.getJSONObject("hier").getJSONArray("content");
            List<JSONObject> list = new ArrayList<>();
            contents.stream().forEach(hier->{
                JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
                if("root".equals(hierJson.getString("parentId"))){
                    JSONObject json = new JSONObject();
                    json.put("bcode",hierJson.getJSONObject("values").getString("material_bcode"));
                    BigDecimal quantity = hierJson.getJSONObject("values").getBigDecimal("quantity")==null?new BigDecimal(0):hierJson.getJSONObject("values").getBigDecimal("quantity");
                    json.put("isTrial",false);
                    json.put("quantity",quantity);
                    codeList.add(json);
                }
            });
        }
        List<JSONObject> ratioList = new ArrayList<>();
        //根据背番分组计算比例
        if(codeList.size()>0) {
            Map<String, List<JSONObject>> codeListByBcode = codeList.stream().collect(Collectors.groupingBy(dto -> dto.getString("bcode")));
            for (Map.Entry<String, List<JSONObject>> entry : codeListByBcode.entrySet()) {
                String bcode = entry.getKey();
                JSONObject json = new JSONObject();
                json.put("bcode", bcode);
                List<JSONObject> list = codeListByBcode.get(bcode);
                //拿到试制数量
                BigDecimal trialQuantity = list.stream().filter(dto -> dto.getBoolean("isTrial"))
                        .map(dto -> dto.getBigDecimal("quantity"))
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                if (!BigDecimal.ZERO.equals(trialQuantity)) {
                    //拿到非试制数量
                    BigDecimal quantity = list.stream().filter(dto -> !dto.getBoolean("isTrial"))
                            .map(dto -> dto.getBigDecimal("quantity"))
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal allQuantity = trialQuantity.add(quantity);
                    BigDecimal trialRatio = trialQuantity.divide(allQuantity, 2, BigDecimal.ROUND_HALF_UP);
                    BigDecimal ratio = new BigDecimal(1).subtract(trialRatio);
                    json.put("trialRatio", trialRatio);
                    json.put("ratio", ratio);
                    ratioList.add(json);
                }
            }
        }
        return ratioList;
    }

    /**
     * 获取废料表数据
     * @param yearMonth
     */
    private List<JSONObject> initWasteData(String yearMonth){
        String con1 = "type='logic' and className='Waste' and yearMonth = '"+yearMonth+"'";
        List<JSONObject> ins = this.logicInstanceDslService.query(con1,"Waste");
        if(ins.size()==0){
            throw new BusinessException(506, yearMonth+"废料表数据没有获取！");
        }
        JSONArray contents = ins.get(0).getJSONObject("hier").getJSONArray("content");
        List<JSONObject> list = new ArrayList<>();
        contents.stream().forEach(hier->{
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            if("lmpvx8v3q1c00".equals(hierJson.getString("parentId"))){
                list.add(hierJson);
            }
        });
        return list;
    }
    /**
     * 获取期初单数据
     *
     */
    private List<JSONObject> initPeriodOrderData(){
        String con1 = "type='logic' and className='periodOrder' ";
        List<JSONObject> ins = this.logicInstanceDslService.query(con1,"periodOrder");
        List<JSONObject> list = new ArrayList<>();
        if(ins.size()>0){
            list = ins.stream().map(dto-> dto.getJSONObject("values")).collect(Collectors.toList());
        }
        return list;
    }
    /**
     * 获取月结表数据
     * @param yearMonth
     */
    private List<JSONObject> initMonthBalanceData(String yearMonth){
        String con1 = "type='logic' and className='MonthBalance' and month = '"+yearMonth+"'";
        List<JSONObject> ins = this.logicInstanceDslService.query(con1,"MonthBalance");
        List<JSONObject> list = new ArrayList<>();
        if(ins.size()>0){
            list = ins.stream().map(dto-> dto.getJSONObject("values")).collect(Collectors.toList());
        }
        return list;
    }
    /**
     * 获取成本分摊数据
     * @param yearMonth
     */
    private List<JSONObject> initCostAllocationData(String yearMonth){
        String con1 = "type='logic' and className='costAllocation' and yearMonth = '"+yearMonth+"'";
        List<JSONObject> ins = this.logicInstanceDslService.query(con1,"costAllocation");
        if(ins.size()==0){
            throw new BusinessException(506, yearMonth+"成本分摊数据没有获取！");
        }
        JSONArray contents = ins.get(0).getJSONObject("hier").getJSONArray("content");
        List<JSONObject> list = new ArrayList<>();
        contents.stream().forEach(hier->{
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            if("4swt9mt6jdi000".equals(hierJson.getString("parentId"))){
                list.add(hierJson);
            }
        });
        return list;
    }
    /**
     * 获取erp单价数据
     * @param yearMonth
     */
    private List<JSONObject> initERPPriceData(String yearMonth){
        String con1 = "type='logic' and className='erpPrice' and yearMonth = '"+yearMonth+"'";
        List<JSONObject> ins = this.logicInstanceDslService.query(con1,"erpPrice");
        if(ins.size()==0){
            throw new BusinessException(506, yearMonth+"ERP数据没有获取！");
        }
        List<JSONObject> list = ins.stream().map(dto-> dto.getJSONObject("values")).collect(Collectors.toList());
        return list;
    }
    /**
     * 获取费用录入数据
     * @param yearMonth
     */
    private JSONObject initCostData(String yearMonth){
        String con1 = "type='logic' and className='Expense' and yearMonth = '"+yearMonth+"'";
        List<JSONObject> costIns = this.logicInstanceDslService.query(con1,"Expense");
        if(costIns.size()==0){
            throw new BusinessException(506, yearMonth+"费用录入数据没有维护！");
        }
        JSONObject costValue = costIns.get(0).getJSONObject("values");

        return costValue;
    }
    /**
     * 获取设备折旧数据
     * @param yearMonth
     */
    private List<JSONObject> initDepreciationList(String yearMonth){
        String con1 = "type='logic' and className='Depreciation' and yearMonth = '"+yearMonth+"'";
        List<JSONObject> equIns = this.logicInstanceDslService.query(con1,"Depreciation");
        if(equIns.size()==0){
            throw new BusinessException("506", yearMonth+"未维护设备折旧数据！");
        }
        JSONArray equHiers = equIns.get(0).getJSONObject("hier").getJSONArray("content");
        List<JSONObject> equList = new ArrayList<>();//人员薪资明细
        Map<Integer,JSONObject> salaryMap = new HashMap<>();
        equHiers.stream().filter(hier->{
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            return "jrjv1qi9zzk00".equals(hierJson.getString("parentId"));
        }).forEach(hier->{
            JSONObject equDepreciation = new JSONObject();
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            String code = hierJson.getJSONObject("values").getString("equCode");
            BigDecimal depreciationMonth = hierJson.getJSONObject("values").getBigDecimal("depreciationMonth");
            equDepreciation.put("id",hierJson.getJSONObject("values").getInteger("equId"));
            equDepreciation.put("code",code);
            equDepreciation.put("depreciationMonth",depreciationMonth);
            equList.add(equDepreciation);
        } );
        return equList;
    }
    /**
     * 获取模具费数据
     * @param yearMonth
     */
    private List<JSONObject> initMouldList(String yearMonth){
        String con1 = "type = 'logic' and className = 'MoldCost' and yearMonth = '"+yearMonth+"'";
        List<JSONObject> equIns = this.logicInstanceDslService.query(con1,"MoldCost");
        if(equIns.size()==0){
            throw new BusinessException("506", yearMonth+"未维护模具费数据！");
        }
        JSONArray mouldHiers = equIns.get(0).getJSONObject("hier").getJSONArray("content");
        List<JSONObject> mouldList = new ArrayList<>();//人员薪资明细
        Map<Integer,JSONObject> salaryMap = new HashMap<>();
        mouldHiers.stream().filter(hier->{
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            return "2zalso9jvfc000".equals(hierJson.getString("parentId"));
        }).forEach(hier->{
            JSONObject mould = new JSONObject();
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            String code = hierJson.getJSONObject("values").getString("code");
            BigDecimal amount = hierJson.getJSONObject("values").getBigDecimal("amount");
            mould.put("code",code);
            mould.put("amount",amount);
            mouldList.add(mould);
        } );
        return mouldList;
    }
    /**
     * 获取人员工资数据
     * @param yearMonth
     * @return
     */
    private List<JSONObject> initSalaryList(String yearMonth){
        String personCon = "type = 'people' and className = 'person'";
        List<JSONObject> personIns = this.resourceInstanceDslService.query(personCon,"person","");
        //获取yearMonth的薪资明细
        String salaryCon = "type = 'logic' and className = 'salaryTable' and yearMonth = '"+yearMonth+"'";
        List<JSONObject> salaryIns = this.logicInstanceDslService.query(salaryCon,"salaryTable","","parentId='4d0o0x8q2dm000'");
        if(salaryIns.size()==0){
            throw new BusinessException("506", yearMonth+"未维护薪资明细！");
        }
        /**
         * 人员属性 attribute：
         * 1 生产人员、2 辅助生产人员、3 管理人员、4 销售人员、5 研发人员
         *
         * 分摊方式 allocationWay：
         * 1 计时、2 计件
         */
        JSONArray salaryHiers = salaryIns.get(0).getJSONObject("hier").getJSONArray("content");
        List<JSONObject> salaryList = new ArrayList<>();//人员薪资明细
        Map<Integer,JSONObject> salaryMap = new HashMap<>();
        salaryHiers.stream().filter(hier->{
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            return "4d0o0x8q2dm000".equals(hierJson.getString("parentId"));
        }).forEach(hier->{
            JSONObject salary = new JSONObject();
            Integer id = 0;
            salary.put("id",id);
            JSONObject hierJson = (JSONObject) JSONObject.toJSON(hier);
            String person_code = hierJson.getJSONObject("values").getString("person_code");
            salary.put("code",person_code);
            List<JSONObject> persons = personIns.stream().filter(per->person_code.equals(per.getJSONObject("values").getString("code"))).collect(Collectors.toList());
            if(persons.size()>0){
                JSONObject person = persons.get(0);
                id = person.getInteger("id");
                salary.put("id",id);
                salary.put("attribute",person.getJSONObject("values").getString("attribute"));
                salary.put("allocationWay",person.getJSONObject("values").getString("allocationWay"));
            }
            BigDecimal netSalary = hierJson.getJSONObject("values").getBigDecimal("netSalary");
            salary.put("netSalary",netSalary);
            salaryList.add(salary);
            salaryMap.put(id,salary);
        } );
        return salaryList;
    }
    private void computeCombinedCost(Map<String,BigDecimal> materialCost,Map<String,ProductBomMaster> combinedBomMap,Map<String,ProductBomMaster> splitBomMap,String bcode){
        if(combinedBomMap.get(bcode)!=null) {//对应组合bom
            //拿到物料如果是组合的看原料是否是原材料

        }else if(splitBomMap.get(bcode)!=null) {//对应拆分bom
            //拿到物料如果是拆分的看原料是否是原材料

        }

    }

    /**
     * 计算相差小时数
     * @param startDateTime
     * @param endDateTime
     * @return
     * @throws ParseException
     */
    private static  BigDecimal getDifferHour(String startDateTime, String endDateTime){
        BigDecimal bwtHours = new BigDecimal(0);
        try{
            Date parse1 = format.parse(startDateTime);
            Date parse2 = format.parse(endDateTime);
            long mills=parse2.getTime()-parse1.getTime();
            //转 BigDecimal
            BigDecimal fz=new BigDecimal(mills);
            //转 BigDecimal
            BigDecimal fm=new BigDecimal(60*60*1000);
            bwtHours=fz.divide(fm,2,BigDecimal.ROUND_HALF_UP);
        }catch (Exception e){
            e.printStackTrace();
        }
        return bwtHours;
    }

}
